<?php

namespace App\Representation\Controller;

use App\Auth\Entity\User;
use App\Auth\Repository\UserRepository;
use App\Location\Repository\CityRepository;
use App\Representation\Entity\Representation;
use App\Representation\Repository\RepresentationRepository;
use App\Representation\Service\JalaliDateService;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Service\InputValidator;
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[OA\Tag(name: 'Representations')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class RepresentationController extends BaseController
{
    public function __construct(
        private readonly RepresentationRepository $representationRepo,
        private readonly UserRepository           $userRepo,
        private readonly CityRepository           $cityRepo,
        private readonly EntityManagerInterface   $em,
        private readonly JalaliDateService        $jalali,
    ) {}

    /**
     * اعمال فیلدهای مشترک create/update روی نماینده.
     * خطا به‌صورت آرایه [code, message, field] برمی‌گردد؛ null یعنی موفق.
     * فیلدهای domain و is_global فقط admin ($isAdmin) مجازند.
     */
    private function applyRepresentationData(Representation $rep, array $data, bool $isAdmin): ?array
    {
        // چند-شهری: city_ids (آرایه) یا city_id قدیمی (BC).
        $cityIds = null;
        if (array_key_exists('city_ids', $data)) {
            $cityIds = is_array($data['city_ids']) ? $data['city_ids'] : [];
        } elseif (array_key_exists('city_id', $data)) {
            $cityIds = $data['city_id'] ? [(int) $data['city_id']] : [];
        }
        if ($cityIds !== null) {
            $cities = [];
            foreach ($cityIds as $cid) {
                $city = $this->cityRepo->find((int) $cid);
                if ($city === null) {
                    return [ErrorCodes::ERR_VALIDATION_001, "شهر با شناسه {$cid} یافت نشد", 'city_ids'];
                }
                $cities[] = $city;
            }
            $rep->setCities($cities);
            $rep->setCityId($cities !== [] ? (int) $cities[0]->getId() : null);
        }

        if (array_key_exists('domain', $data)) {
            if (!$isAdmin) {
                return [ErrorCodes::ERR_AUTH_006, 'تغییر دامنه فقط توسط مدیر مجاز است', 'domain'];
            }
            $domain = Representation::normalizeDomain(is_string($data['domain']) ? $data['domain'] : null);
            if ($domain !== null) {
                if (!preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $domain)) {
                    return [ErrorCodes::ERR_VALIDATION_001, 'دامنه نامعتبر است', 'domain'];
                }
                if ($this->cityRepo->findByDomain($domain) !== null) {
                    return [ErrorCodes::ERR_CONFLICT_001, 'این دامنه متعلق به یکی از شهرهاست', 'domain'];
                }
                $existing = $this->representationRepo->findOneBy(['domain' => $domain]);
                if ($existing !== null && $existing->getId() !== $rep->getId()) {
                    return [ErrorCodes::ERR_CONFLICT_001, 'این دامنه قبلاً برای نماینده دیگری ثبت شده است', 'domain'];
                }
            }
            $rep->setDomain($domain);
        }

        if (array_key_exists('is_global', $data)) {
            if (!$isAdmin) {
                return [ErrorCodes::ERR_AUTH_006, 'تغییر نوع نماینده (سراسری) فقط توسط مدیر مجاز است', 'is_global'];
            }
            $rep->setIsGlobal((bool) $data['is_global']);
        }

        return null;
    }

    // ── CRUD ──────────────────────────────────────────────────────────────────

    #[OA\Post(
        path: '/api/v1/representation',
        summary: 'Create a new representation (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['full_name', 'mobile_number'],
                properties: [
                    new OA\Property(property: 'full_name', type: 'string'),
                    new OA\Property(property: 'mobile_number', type: 'string'),
                    new OA\Property(property: 'city_id', type: 'integer', nullable: true),
                    new OA\Property(property: 'commission_percent', type: 'number', format: 'float', nullable: true),
                    new OA\Property(property: 'bank_account', type: 'object', nullable: true),
                ]
            )
        ),
        responses: [
            new OA\Response(
                response: 201,
                description: 'Representation created',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(
                response: 409,
                description: 'Conflict – user is already a representation',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: false),
                        new OA\Property(property: 'errors', type: 'array', items: new OA\Items(
                            properties: [
                                new OA\Property(property: 'code', type: 'string'),
                                new OA\Property(property: 'message', type: 'string'),
                            ]
                        )),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
            new OA\Response(response: 422, description: 'Validation error'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/representation', methods: ['POST'])]
    public function create(Request $request): JsonResponse
    {
        $data     = json_decode($request->getContent(), true) ?? [];
        $mobile   = InputValidator::toEnglishDigits(trim((string) ($data['mobile_number'] ?? '')));
        $fullName = trim((string) ($data['full_name'] ?? ''));

        if (empty($mobile) || empty($fullName)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'mobile_number و full_name الزامی است', 422);
        }

        if (!InputValidator::isValidIranMobile($mobile)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره موبایل نامعتبر است', 422, 'mobile_number');
        }

        $user = $this->userRepo->findByMobile($mobile);
        if ($user === null) {
            $user = new User($mobile);
            $this->em->persist($user);
        }
        $user->addRole('ROLE_REPRESENTATION');
        $this->em->flush();

        if ($this->representationRepo->findByUser($user) !== null) {
            return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این کاربر قبلاً نماینده است', 409);
        }

        if (!empty($data['commission_percent'])) {
            $commission = (float) $data['commission_percent'];
            if ($commission < 0 || $commission > 100) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'درصد کمیسیون باید بین ۰ تا ۱۰۰ باشد', 422, 'commission_percent');
            }
        }

        $rep = new Representation($user, $fullName);
        if (!empty($data['commission_percent'])) $rep->setCommissionPercent((string)$data['commission_percent']);
        if (!empty($data['bank_account']))       $rep->setBankAccount($data['bank_account']);

        // create فقط توسط ادمین انجام می‌شود (IsGranted بالای متد) → isAdmin=true.
        if (($err = $this->applyRepresentationData($rep, $data, true)) !== null) {
            [$code, $message, $field] = $err;
            $status = $code === ErrorCodes::ERR_CONFLICT_001 ? 409 : 422;
            return $this->error($code, $message, $status, $field);
        }

        $this->representationRepo->save($rep);

        return $this->success(['data' => $rep->toArray()], 201);
    }

    #[OA\Get(
        path: '/api/v1/representation/{uuid}',
        summary: 'Get a representation by UUID (own or admin)',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Representation details',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[Route('/api/v1/representation/{uuid}', methods: ['GET'])]
    public function get(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $rep = $this->representationRepo->findByUuid($uuid);
        if ($rep === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
        }

        if ($rep->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        return $this->success(['data' => $rep->toArray()]);
    }

    #[OA\Patch(
        path: '/api/v1/representation/{uuid}',
        summary: 'Update a representation (own or admin)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: false,
            content: new OA\JsonContent(
                properties: [
                    new OA\Property(property: 'full_name', type: 'string'),
                    new OA\Property(property: 'city_id', type: 'integer', nullable: true),
                    new OA\Property(property: 'bank_account', type: 'object', nullable: true),
                    new OA\Property(property: 'commission_percent', type: 'number', format: 'float'),
                    new OA\Property(property: 'active', type: 'boolean'),
                ]
            )
        ),
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Representation updated',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[Route('/api/v1/representation/{uuid}', methods: ['PATCH'])]
    public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $rep = $this->representationRepo->findByUuid($uuid);
        if ($rep === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
        }

        if ($rep->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data  = json_decode($request->getContent(), true) ?? [];
        $isAdmin = $user->hasRole('ROLE_ADMIN');

        if (array_key_exists('full_name', $data))    $rep->setFullName($data['full_name']);
        if (array_key_exists('bank_account', $data)) $rep->setBankAccount($data['bank_account']);

        if (($err = $this->applyRepresentationData($rep, $data, $isAdmin)) !== null) {
            [$code, $message, $field] = $err;
            $status = match ($code) {
                ErrorCodes::ERR_CONFLICT_001 => 409,
                ErrorCodes::ERR_AUTH_006     => 403,
                default                      => 422,
            };
            return $this->error($code, $message, $status, $field);
        }

        // commission_percent and active are privileged: a representative must not
        // be able to raise their own commission or activate themselves.
        if (array_key_exists('commission_percent', $data)) {
            if (!$isAdmin) {
                return $this->error(ErrorCodes::ERR_AUTH_006, 'تغییر درصد کمیسیون فقط توسط مدیر مجاز است', 403);
            }
            $commission = (float) $data['commission_percent'];
            if ($commission < 0 || $commission > 100) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'درصد کمیسیون باید بین ۰ تا ۱۰۰ باشد', 422, 'commission_percent');
            }
            $rep->setCommissionPercent((string) $commission);
        }
        if (array_key_exists('active', $data)) {
            if (!$isAdmin) {
                return $this->error(ErrorCodes::ERR_AUTH_006, 'تغییر وضعیت فعال‌بودن فقط توسط مدیر مجاز است', 403);
            }
            $rep->setActive((bool) $data['active']);
        }

        $this->representationRepo->save($rep);

        return $this->success(['data' => $rep->toArray()]);
    }

    #[OA\Delete(
        path: '/api/v1/representation/{uuid}',
        summary: 'Delete a representation (ROLE_ADMIN)',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Representation deleted',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', properties: [
                            new OA\Property(property: 'message', type: 'string'),
                        ], type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden – ROLE_ADMIN required'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[IsGranted('ROLE_ADMIN')]
    #[Route('/api/v1/representation/{uuid}', methods: ['DELETE'])]
    public function delete(string $uuid): JsonResponse
    {
        $rep = $this->representationRepo->findByUuid($uuid);
        if ($rep === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
        }

        $this->representationRepo->remove($rep);
        return $this->success(['message' => 'نماینده با موفقیت حذف شد']);
    }

    // ── Dashboard: monthly stats ──────────────────────────────────────────────

    #[OA\Get(
        path: '/api/v1/representation/{uuid}/dashboard/monthly',
        summary: 'Get monthly dashboard stats for a representation',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
            new OA\Parameter(name: 'year', in: 'query', required: false, description: 'Jalali year', schema: new OA\Schema(type: 'integer')),
            new OA\Parameter(name: 'month', in: 'query', required: false, description: 'Jalali month (1–12)', schema: new OA\Schema(type: 'integer', minimum: 1, maximum: 12)),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Monthly statistics',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', properties: [
                            new OA\Property(property: 'period', properties: [
                                new OA\Property(property: 'jalali_year', type: 'integer'),
                                new OA\Property(property: 'jalali_month', type: 'integer'),
                            ], type: 'object'),
                            new OA\Property(property: 'stats', properties: [
                                new OA\Property(property: 'total_payments', type: 'integer'),
                                new OA\Property(property: 'total_revenue_rials', type: 'integer'),
                                new OA\Property(property: 'total_appointments', type: 'integer'),
                            ], type: 'object'),
                        ], type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[Route('/api/v1/representation/{uuid}/dashboard/monthly', methods: ['GET'])]
    public function dashboardMonthly(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $rep = $this->representationRepo->findByUuid($uuid);
        if ($rep === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
        }

        if ($rep->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $jYear  = (int) ($request->query->get('year',  $this->jalali->jalaliYear(time())));
        $jMonth = (int) ($request->query->get('month', $this->jalali->jalaliMonth(time())));

        [$startTs, $endTs] = $this->jalali->jalaliMonthRange($jYear, $jMonth);

        return $this->success([
            'period' => ['jalali_year' => $jYear, 'jalali_month' => $jMonth],
            'stats'  => $this->buildStats($rep, $startTs, $endTs),
        ]);
    }

    #[OA\Get(
        path: '/api/v1/representation/{uuid}/dashboard/yearly',
        summary: 'Get yearly dashboard stats for a representation',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
            new OA\Parameter(name: 'year', in: 'query', required: false, description: 'Jalali year', schema: new OA\Schema(type: 'integer')),
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: 'Yearly statistics broken down by month',
                content: new OA\JsonContent(
                    properties: [
                        new OA\Property(property: 'success', type: 'boolean', example: true),
                        new OA\Property(property: 'data', properties: [
                            new OA\Property(property: 'period', properties: [
                                new OA\Property(property: 'jalali_year', type: 'integer'),
                            ], type: 'object'),
                            new OA\Property(property: 'months', type: 'array', items: new OA\Items(
                                properties: [
                                    new OA\Property(property: 'jalali_month', type: 'integer'),
                                    new OA\Property(property: 'stats', properties: [
                                        new OA\Property(property: 'total_payments', type: 'integer'),
                                        new OA\Property(property: 'total_revenue_rials', type: 'integer'),
                                        new OA\Property(property: 'total_appointments', type: 'integer'),
                                    ], type: 'object'),
                                ]
                            )),
                            new OA\Property(property: 'totals', properties: [
                                new OA\Property(property: 'total_payments', type: 'integer'),
                                new OA\Property(property: 'total_revenue_rials', type: 'integer'),
                                new OA\Property(property: 'total_appointments', type: 'integer'),
                            ], type: 'object'),
                        ], type: 'object'),
                    ]
                )
            ),
            new OA\Response(response: 401, description: 'Unauthorized'),
            new OA\Response(response: 403, description: 'Forbidden'),
            new OA\Response(response: 404, description: 'Not found'),
        ]
    )]
    #[Route('/api/v1/representation/{uuid}/dashboard/yearly', methods: ['GET'])]
    public function dashboardYearly(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $rep = $this->representationRepo->findByUuid($uuid);
        if ($rep === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'نماینده یافت نشد', 404);
        }

        if ($rep->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $jYear = (int) ($request->query->get('year', $this->jalali->jalaliYear(time())));

        $months = [];
        for ($m = 1; $m <= 12; $m++) {
            [$mStart, $mEnd] = $this->jalali->jalaliMonthRange($jYear, $m);
            $months[]        = [
                'jalali_month' => $m,
                'stats'        => $this->buildStats($rep, $mStart, $mEnd),
            ];
        }

        [$startTs, $endTs] = $this->jalali->jalaliYearRange($jYear);

        return $this->success([
            'period' => ['jalali_year' => $jYear],
            'months' => $months,
            'totals' => $this->buildStats($rep, $startTs, $endTs),
        ]);
    }

    // ── Private ───────────────────────────────────────────────────────────────

    private function buildStats(Representation $rep, int $startTs, int $endTs): array
    {
        $repId = $rep->getId();

        // نوبت‌های پزشکانِ همین نماینده در بازه.
        $totalAppointments = (int) $this->em->createQuery(
            'SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
             JOIN a.doctor d
             WHERE d.representationId = :repId AND a.createdAt BETWEEN :start AND :end'
        )->setParameters(['repId' => $repId, 'start' => $startTs, 'end' => $endTs])
         ->getSingleScalarResult();

        // درآمد واقعیِ ثبت‌شده برای نماینده (سهم نماینده از FinancialBreakdown)، نه مبلغ کل نوبت.
        $commission = (int) ($this->em->createQuery(
            'SELECT SUM(b.representationShareRials) FROM App\Settlement\Entity\FinancialBreakdown b
             WHERE b.representationId = :repId AND b.createdAt BETWEEN :start AND :end'
        )->setParameters(['repId' => $repId, 'start' => $startTs, 'end' => $endTs])
         ->getSingleScalarResult() ?? 0);

        // مبلغ کلِ نوبت‌های مشمول پورسانتِ همین نماینده در بازه (برای اطلاع).
        $totalRevenue = (int) ($this->em->createQuery(
            'SELECT SUM(b.grossRials) FROM App\Settlement\Entity\FinancialBreakdown b
             WHERE b.representationId = :repId AND b.source = :src AND b.createdAt BETWEEN :start AND :end'
        )->setParameters(['repId' => $repId, 'src' => 'appointment', 'start' => $startTs, 'end' => $endTs])
         ->getSingleScalarResult() ?? 0);

        return [
            'total_appointments'  => $totalAppointments,
            'total_revenue_rials' => $totalRevenue,
            'commission_rials'    => $commission,
        ];
    }
}
