<?php

namespace App\Secretary\Controller;

use App\Auth\Entity\User;
use App\Auth\Repository\UserRepository;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Secretary\Entity\DoctorSecretary;
use App\Shared\Context\EntityContext;
use App\Secretary\Repository\DoctorSecretaryRepository;
use App\Secretary\Service\SecretaryService;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Sms\Entity\SmsLog;
use App\Sms\Service\SmsService;
use App\Subscription\Service\SubscriptionService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'Secretaries')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class SecretaryController extends BaseController
{
    public function __construct(
        private readonly DoctorSecretaryRepository   $secretaryRepo,
        private readonly DoctorRepository            $doctorRepo,
        private readonly ClinicRepository            $clinicRepo,
        private readonly UserRepository              $userRepo,
        private readonly UserPasswordHasherInterface $hasher,
        private readonly SubscriptionService         $subscriptionService,
        private readonly SmsService                  $smsService,
        private readonly SecretaryService            $secretaryService,
        private readonly \App\Secretary\Repository\SecretaryEarningRepository $earningRepo,
        private readonly \App\UserProfile\Repository\UserProfileRepository $profileRepo,
        private readonly \App\Settlement\Repository\SettlementRepository $settlementRepo,
        private readonly string                      $appUrl,
    ) {}

    // ── درآمد منشی از نوبت‌های آنلاین ─────────────────────────────────────────

    /**
     * رابطه‌های فعالی که سهم نوبت آنلاین برایشان روشن است.
     *
     * @return DoctorSecretary[]
     */
    private function shareRelationsOf(User $user): array
    {
        return array_values(array_filter(
            $this->secretaryRepo->findAllActiveBySecretary($user),
            static fn(DoctorSecretary $r) => $r->effectiveOnlineSharePercent() > 0,
        ));
    }

    #[OA\Get(
        path: '/api/v1/secretary/earnings/summary',
        summary: 'Secretary earnings summary (today / last 30 days / total) from online appointments',
        security: [['bearerAuth' => []]],
        responses: [new OA\Response(response: 200, description: 'Earnings summary')]
    )]
    #[Route('/api/v1/secretary/earnings/summary', methods: ['GET'])]
    public function earningsSummary(#[CurrentUser] User $user): JsonResponse
    {
        $relations = $this->shareRelationsOf($user);
        $today     = strtotime('today');

        return $this->success([
            'data' => [
                // false یعنی این قابلیت برای هیچ رابطه‌ای فعال نیست؛ پنل پیام مناسب نشان می‌دهد.
                'enabled'             => $relations !== [],
                'share_percent'       => $relations !== [] ? $relations[0]->effectiveOnlineSharePercent() : 0.0,
                'relations'           => array_map(static fn(DoctorSecretary $r) => [
                    'relation_uuid' => $r->getUuid(),
                    'doctor_name'   => $r->getDoctor()->getName(),
                    'clinic_name'   => $r->getClinic()?->getName(),
                    'share_percent' => $r->effectiveOnlineSharePercent(),
                ], $relations),
                'today_rials'         => $this->earningRepo->sumFor($user, $today),
                'this_month_rials'    => $this->earningRepo->sumFor($user, time() - 30 * 86_400),
                'total_rials'         => $this->earningRepo->sumFor($user),
                'appointments_count'  => $this->earningRepo->countFor($user),
                'wallet_balance_rials' => $this->settlementRepo->getWalletBalance($user),
            ],
        ]);
    }

    #[OA\Get(
        path: '/api/v1/secretary/earnings/report',
        summary: 'Paginated per-appointment earnings report of the current secretary',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)),
            new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 15)),
            new OA\Parameter(name: 'from', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
            new OA\Parameter(name: 'to', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
        ],
        responses: [new OA\Response(response: 200, description: 'Paginated earnings rows')]
    )]
    #[Route('/api/v1/secretary/earnings/report', methods: ['GET'])]
    public function earningsReport(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(100, max(1, (int) $request->query->get('limit', 15)));
        $from  = $request->query->get('from');
        $to    = $request->query->get('to');

        $report = $this->earningRepo->reportFor(
            $user,
            $page,
            $limit,
            ($from !== null && $from !== '') ? (int) $from : null,
            ($to !== null && $to !== '') ? (int) $to : null,
        );

        return $this->paginated($report['items'], $report['total'], $page, $limit);
    }

    // ── شماره شبای منشی (برای تسویه) ──────────────────────────────────────────

    #[OA\Post(
        path: '/api/v1/secretary/iban',
        summary: 'Add an IBAN (max 2) to the current secretary profile',
        security: [['bearerAuth' => []]],
        responses: [
            new OA\Response(response: 201, description: 'IBAN added'),
            new OA\Response(response: 422, description: 'Invalid IBAN or limit reached'),
        ]
    )]
    #[Route('/api/v1/secretary/iban', methods: ['POST'])]
    public function addIban(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data = json_decode($request->getContent(), true) ?? [];
        $iban = strtoupper(preg_replace('/\s+/', '', (string) ($data['iban'] ?? '')));

        if (!preg_match('/^IR\d{24}$/', $iban)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره شبا نامعتبر است (IR و ۲۴ رقم)', 422, 'iban');
        }

        $profile = $this->profileRepo->findByUser($user) ?? new \App\UserProfile\Entity\UserProfile($user);

        try {
            // verified فقط از سمت ادمین ست می‌شود؛ تسویه تنها با شبای تأییدشده مجاز است.
            $profile->addIban([
                'iban'       => $iban,
                'bank_name'  => isset($data['bank_name']) ? trim((string) $data['bank_name']) : null,
                'owner_name' => isset($data['owner_name']) ? trim((string) $data['owner_name']) : null,
            ]);
        } catch (\DomainException) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'حداکثر دو شماره شبا مجاز است', 422, 'iban');
        }

        $this->profileRepo->save($profile);

        return $this->success(['data' => ['bank_account' => $profile->getIbans()]], 201);
    }

    #[OA\Delete(
        path: '/api/v1/secretary/iban/{id}',
        summary: 'Remove one of the current secretary IBANs',
        security: [['bearerAuth' => []]],
        responses: [new OA\Response(response: 200, description: 'IBAN removed')]
    )]
    #[Route('/api/v1/secretary/iban/{id}', methods: ['DELETE'])]
    public function removeIban(string $id, #[CurrentUser] User $user): JsonResponse
    {
        $profile = $this->profileRepo->findByUser($user);
        if ($profile === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'شماره شبا یافت نشد', 404);
        }

        $this->profileRepo->save($profile->removeIban($id));

        return $this->success(['data' => ['bank_account' => $profile->getIbans()]]);
    }

    #[OA\Get(
        path: '/api/v1/secretary/me',
        summary: 'Current secretary profile: relations, share settings and IBANs',
        security: [['bearerAuth' => []]],
        responses: [new OA\Response(response: 200, description: 'Secretary profile')]
    )]
    #[Route('/api/v1/secretary/me', methods: ['GET'])]
    public function me(#[CurrentUser] User $user): JsonResponse
    {
        $relations = $this->secretaryRepo->findAllActiveBySecretary($user);

        return $this->success([
            'data' => [
                'full_name'    => $user->getRealName(),
                'mobile'       => $user->getMobileNumber(),
                'bank_account' => $this->profileRepo->findByUser($user)?->getIbans() ?? [],
                'relations'    => array_map(static fn(DoctorSecretary $r) => [
                    'relation_uuid'        => $r->getUuid(),
                    'doctor_name'          => $r->getDoctor()->getName(),
                    'clinic_name'          => $r->getClinic()?->getName(),
                    'online_share_enabled' => $r->isOnlineShareEnabled(),
                    'online_share_percent' => $r->getOnlineSharePercent(),
                ], $relations),
            ],
        ]);
    }

    #[Route('/api/v1/secretary', methods: ['POST'])]
    public function create(Request $request, #[CurrentUser] User $currentUser): JsonResponse
    {
        $data       = json_decode($request->getContent(), true) ?? [];
        $doctorUuid = trim($data['doctor_uuid'] ?? '');
        $mobile     = trim($data['mobile_number'] ?? '');

        // تخصیص چند‌پزشکی توسط مدیر کلینیک
        $doctorUuids = $data['doctor_uuids'] ?? null;
        if (is_array($doctorUuids) && $doctorUuids !== []) {
            return $this->createForClinicDoctors($currentUser, $mobile, $doctorUuids, $data);
        }

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

        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        // تعیین scope بر اساس نقش کاربر جاری
        $ownerClinic = null;
        if ($currentUser->hasRole('ROLE_CLINIC')) {
            $ownerClinic = $this->clinicRepo->findByUser($currentUser);
            if ($ownerClinic === null || !$this->secretaryRepo->isDoctorInClinic($doctor, $ownerClinic)) {
                return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
            }
        } elseif (!$currentUser->hasRole('ROLE_ADMIN')) {
            // دکتر فقط برای خودش منشی تعریف می‌کند
            if ($doctor->getUser()->getId() !== $currentUser->getId()) {
                return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
            }
        }

        // Check plan limit
        $limit       = $this->subscriptionService->getSecretaryLimit('doctor', $doctor->getId());
        $activeCount = $this->secretaryRepo->countActiveByDoctor($doctor);
        if ($activeCount >= $limit) {
            return $this->error(ErrorCodes::ERR_SECRETARY_001, ErrorCodes::message(ErrorCodes::ERR_SECRETARY_001), 422);
        }

        // Find or create secretary user
        $secretaryUser = $this->userRepo->findByMobile($mobile);
        if ($secretaryUser === null) {
            $secretaryUser = new User($mobile);
            if (!empty($data['password'])) {
                $hash = $this->hasher->hashPassword($secretaryUser, $data['password']);
                $secretaryUser->setPasswordHash($hash);
            }
        }
        if (!empty($data['name'])) {
            $secretaryUser->setRealName(trim($data['name']));
        }

        // Assign ROLE_SECRETARY
        $roles = $secretaryUser->getRoles();
        if (!in_array('ROLE_SECRETARY', $roles, true)) {
            $roles[] = 'ROLE_SECRETARY';
            $secretaryUser->setRoles(array_values(array_unique($roles)));
        }
        $this->userRepo->save($secretaryUser);

        // تکراری فقط در همان محیط. entityId هم لازم است، وگرنه تخصیص همان منشی به
        // همان پزشک در کلینیک دوم — که کلید یکتا اجازه‌اش را می‌دهد — اینجا رد می‌شد.
        [$ownerEntityType, $ownerEntityId] = EntityContext::forBooking($doctor, $ownerClinic)->toEntityPair();

        $existing = $this->secretaryRepo->findOneBy([
            'doctor'     => $doctor,
            'secretary'  => $secretaryUser,
            'entityType' => $ownerEntityType,
            'entityId'   => $ownerEntityId,
        ]);
        if ($existing !== null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این منشی قبلاً اضافه شده است', 409);
        }

        $secretary = new DoctorSecretary($doctor, $secretaryUser, $ownerClinic);

        if (array_key_exists('national_code', $data)) {
            $secretary->setNationalCode($data['national_code'] !== null ? trim((string) $data['national_code']) : null);
        }
        if (array_key_exists('address', $data)) {
            $secretary->setAddress($data['address'] !== null ? trim((string) $data['address']) : null);
        }

        if (!empty($data['permissions'])) {
            $secretary->mergePermissions($data['permissions']);
        }

        $this->secretaryRepo->save($secretary);

        $this->sendWelcomeSms($mobile, $doctor, $ownerClinic);

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

    /** پیامک خوش‌آمد به منشی تازه‌تعریف‌شده — متن از template تگ TAG_SECRETARY */
    private function sendWelcomeSms(string $mobile, $doctor, $ownerClinic): void
    {
        $ownerName = $ownerClinic !== null
            ? ($ownerClinic->getName() ?? 'کلینیک')
            : ($doctor->getUser()->getRealName() ?? 'پزشک');

        $link = rtrim($this->appUrl, '/') . '/login';

        $this->smsService->dispatchTemplate(SmsLog::TAG_SECRETARY, $mobile, [
            'owner'    => $ownerName,
            'username' => $mobile,
            'link'     => $link,
        ]);
    }

    #[Route('/api/v1/secretary/{uuid}', methods: ['GET'])]
    public function show(string $uuid, #[CurrentUser] User $currentUser): JsonResponse
    {
        $secretary = $this->secretaryRepo->findByUuid($uuid);
        if ($secretary === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'منشی یافت نشد', 404);
        }

        if (!$this->canManage($secretary, $currentUser)) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

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

    #[Route('/api/v1/secretary/{uuid}', methods: ['PATCH'])]
    public function update(string $uuid, Request $request, #[CurrentUser] User $currentUser): JsonResponse
    {
        $secretary = $this->secretaryRepo->findByUuid($uuid);
        if ($secretary === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'منشی یافت نشد', 404);
        }

        if (!$this->canManage($secretary, $currentUser)) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data = json_decode($request->getContent(), true) ?? [];

        if (array_key_exists('active', $data)) {
            $secretary->setActive((bool) $data['active']);
        }

        if (!empty($data['name'])) {
            $secretaryUser = $secretary->getSecretary();
            $secretaryUser->setRealName(trim((string) $data['name']));
            $this->userRepo->save($secretaryUser);
        }

        if (array_key_exists('national_code', $data)) {
            $secretary->setNationalCode($data['national_code'] !== null ? trim((string) $data['national_code']) : null);
        }
        if (array_key_exists('address', $data)) {
            $secretary->setAddress($data['address'] !== null ? trim((string) $data['address']) : null);
        }

        if (!empty($data['permissions'])) {
            $secretary->mergePermissions($data['permissions']);
        }

        $this->secretaryRepo->save($secretary);

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

    #[Route('/api/v1/secretary/{uuid}', methods: ['DELETE'])]
    public function deactivate(string $uuid, #[CurrentUser] User $currentUser): JsonResponse
    {
        $secretary = $this->secretaryRepo->findByUuid($uuid);
        if ($secretary === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'منشی یافت نشد', 404);
        }

        if (!$this->canManage($secretary, $currentUser)) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $secretary->setActive(false);
        $this->secretaryRepo->save($secretary);

        return $this->success(['message' => 'منشی غیرفعال شد']);
    }

    /** لیست منشی ها مطب شخصی یک دکتر */
    #[Route('/api/v1/secretaries/{doctorUuid}', methods: ['GET'])]
    public function list(string $doctorUuid, #[CurrentUser] User $currentUser): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        // فقط خود دکتر یا ادمین می‌توانند منشی ها مطب شخصی را ببینند
        if (!$currentUser->hasRole('ROLE_ADMIN') && $doctor->getUser()->getId() !== $currentUser->getId()) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $secretaries = array_map(
            fn(DoctorSecretary $s) => $s->toArray(),
            $this->secretaryRepo->findByDoctorScope($doctor)
        );

        return $this->success($secretaries);
    }

    /** لیست همه منشی ها کلینیک (از همه دکترها، owner_type='clinic') */
    #[Route('/api/v1/secretaries/clinic/{clinicUuid}', methods: ['GET'])]
    public function listByClinic(string $clinicUuid, #[CurrentUser] User $currentUser): JsonResponse
    {
        $clinic = $this->clinicRepo->findByUuid($clinicUuid);
        if ($clinic === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
        }

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

        $secretaries = array_map(
            fn(DoctorSecretary $s) => $s->toArray(),
            $this->secretaryRepo->findByClinic($clinic)
        );

        return $this->success($secretaries);
    }

    /** تخصیص یک منشی به چند پزشکِ کلینیک (فقط مدیر کلینیک) */
    private function createForClinicDoctors(User $currentUser, string $mobile, array $doctorUuids, array $data): JsonResponse
    {
        if (empty($mobile)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'mobile_number الزامی است', 422);
        }
        if (!$currentUser->hasRole('ROLE_CLINIC')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }
        $clinic = $this->clinicRepo->findByUser($currentUser);
        if ($clinic === null) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $result = $this->secretaryService->assignToClinicDoctors($clinic, $mobile, $doctorUuids, [
            'name'          => $data['name'] ?? null,
            'password'      => $data['password'] ?? null,
            'national_code' => $data['national_code'] ?? null,
            'address'       => $data['address'] ?? null,
            'permissions'   => $data['permissions'] ?? null,
        ]);

        return $this->success([
            'secretary_uuid'        => $result['secretary']->getUuid(),
            'created'               => array_map(fn(DoctorSecretary $s) => $s->toArray(), $result['created']),
            'skipped_duplicate'     => $result['skipped_duplicate'],
            'skipped_limit'         => $result['skipped_limit'],
            'skipped_not_in_clinic' => $result['skipped_not_in_clinic'],
        ], 201);
    }

    /** هم‌گام‌سازی مجموعه‌ی پزشکانِ یک منشیِ کلینیک */
    #[Route('/api/v1/secretaries/clinic/{clinicUuid}/doctors', methods: ['PUT'])]
    public function syncClinicDoctors(string $clinicUuid, Request $request, #[CurrentUser] User $currentUser): JsonResponse
    {
        $clinic = $this->clinicRepo->findByUuid($clinicUuid);
        if ($clinic === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
        }
        if ($clinic->getUser()->getId() !== $currentUser->getId() && !$currentUser->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data          = json_decode($request->getContent(), true) ?? [];
        $secretaryUuid = trim($data['secretary_uuid'] ?? '');
        $doctorUuids   = $data['doctor_uuids'] ?? null;
        if (empty($secretaryUuid) || !is_array($doctorUuids)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'secretary_uuid و doctor_uuids الزامی است', 422);
        }

        $secretaryUser = $this->userRepo->findByUuid($secretaryUuid);
        if ($secretaryUser === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'منشی یافت نشد', 404);
        }

        $result = $this->secretaryService->syncClinicDoctors($clinic, $secretaryUser, $doctorUuids);

        return $this->success([
            'added'                 => count($result['added']),
            'removed'               => count($result['removed']),
            'skipped_limit'         => $result['skipped_limit'],
            'skipped_not_in_clinic' => $result['skipped_not_in_clinic'],
        ]);
    }

    /** بررسی دسترسی برای ویرایش/حذف یک رابطه منشی — scope-aware */
    private function canManage(DoctorSecretary $secretary, User $user): bool
    {
        if ($user->hasRole('ROLE_ADMIN')) {
            return true;
        }

        if ($secretary->getOwnerType() === DoctorSecretary::OWNER_DOCTOR) {
            // فقط خود دکتر می‌تواند منشی مطب شخصی‌اش را مدیریت کند
            return $secretary->getDoctor()->getUser()->getId() === $user->getId();
        }

        if ($secretary->getOwnerType() === DoctorSecretary::OWNER_CLINIC) {
            // فقط مالک کلینیک می‌تواند منشی ها کلینیکی را مدیریت کند
            if (!$user->hasRole('ROLE_CLINIC')) {
                return false;
            }
            $clinic = $this->clinicRepo->findByUser($user);
            return $clinic !== null && $secretary->getClinic()?->getId() === $clinic->getId();
        }

        return false;
    }
}
