<?php

namespace App\Doctor\Controller;

use App\Auth\Entity\User;
use App\Doctor\Repository\DoctorRepository;
use App\Doctor\Service\DoctorClaimService;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[OA\Tag(name: 'Doctors')]
class DoctorClaimController extends BaseController
{
    public function __construct(
        private readonly DoctorRepository   $doctorRepo,
        private readonly DoctorClaimService $claimService,
        private readonly RateLimiterFactory $doctorClaimLimiter,
        private readonly \App\Doctor\Repository\DoctorClaimRequestRepository $claimRepo,
        private readonly \App\Shared\Captcha\CaptchaGuard $captcha,
    ) {}

    #[OA\Get(
        path: '/api/v1/doctor/{uuid}/claim-info',
        summary: 'Whether this doctor profile can be claimed (public, renders the claim button)',
        responses: [
            new OA\Response(response: 200, description: '{ claimable, owner_status }'),
            new OA\Response(response: 404, description: 'Doctor not found'),
        ]
    )]
    #[Route('/api/v1/doctor/{uuid}/claim-info', methods: ['GET'])]
    public function claimInfo(string $uuid): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($uuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
        }

        return $this->success([
            'claimable'    => $doctor->getOwnerStatus() === 'unclaimed',
            'owner_status' => $doctor->getOwnerStatus(),
        ]);
    }

    #[OA\Post(
        path: '/api/v1/doctor/{uuid}/claim',
        summary: 'Claim an unclaimed (IRIMC-imported) doctor profile after identity verification',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['national_code', 'birth_date', 'first_name', 'last_name'],
                properties: [
                    new OA\Property(property: 'national_code', type: 'string', example: '0010007700'),
                    new OA\Property(property: 'birth_date', type: 'string', description: 'شمسی Y/m/d', example: '1371/1/1'),
                    new OA\Property(property: 'first_name', type: 'string'),
                    new OA\Property(property: 'last_name', type: 'string'),
                ]
            )
        ),
        responses: [
            new OA\Response(response: 200, description: 'Claimed'),
            new OA\Response(response: 409, description: 'Not claimable / user already owns a doctor'),
            new OA\Response(response: 422, description: 'Validation or identity mismatch'),
            new OA\Response(response: 429, description: 'Rate limited'),
            new OA\Response(response: 502, description: 'Identity provider unavailable'),
        ]
    )]
    #[Route('/api/v1/doctor/{uuid}/claim', methods: ['POST'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function claim(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $limiter = $this->doctorClaimLimiter->create('claim_' . $user->getId() . '_' . $uuid);
        $limit   = $limiter->consume();
        if (!$limit->isAccepted()) {
            throw new TooManyRequestsHttpException($limit->getRetryAfter()->getTimestamp() - time());
        }

        // کپچای ALTCHA (در dev با ALTCHA_ENABLED=false بی‌اثر) — خطا → ERR_CAPTCHA_001 (422)
        $this->captcha->assertValid($request);

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

        $data         = json_decode($request->getContent(), true) ?? [];
        $nationalCode = preg_replace('/\D/', '', \App\Shared\Util\PersianText::normalize((string) ($data['national_code'] ?? '')));
        $birthDate    = trim(\App\Shared\Util\PersianText::normalize((string) ($data['birth_date'] ?? '')));
        $firstName    = trim((string) ($data['first_name'] ?? ''));
        $lastName     = trim((string) ($data['last_name'] ?? ''));
        $mobile       = preg_replace('/\D/', '', \App\Shared\Util\PersianText::normalize((string) ($data['mobile'] ?? '')));

        if ($mobile !== '' && $mobile !== $user->getMobileNumber()) {
            return $this->error(ErrorCodes::ERR_CONFLICT_001, 'شماره موبایل باید با حساب کاربری شما یکی باشد', 422, 'mobile');
        }
        if (!preg_match('/^\d{10}$/', $nationalCode)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'کد ملی نامعتبر است', 422, 'national_code');
        }
        if (!preg_match('~^1[34]\d{2}/\d{1,2}/\d{1,2}$~', $birthDate)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'تاریخ تولد نامعتبر است (مثال: 1371/1/1)', 422, 'birth_date');
        }
        if ($firstName === '' || $lastName === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نام و نام خانوادگی الزامی است', 422);
        }

        $claim = $this->claimService->claim($doctor, $user, $nationalCode, $birthDate, $firstName, $lastName);

        return $this->success([
            'status' => 'claimed',
            'claim'  => ['uuid' => $claim->getUuid()],
            'doctor' => ['uuid' => $doctor->getUuid(), 'name' => $doctor->getName()],
        ]);
    }

    #[OA\Post(
        path: '/api/v1/admin/doctors/{uuid}/transfer',
        summary: 'Manually transfer an unclaimed doctor profile to a real user (admin support tool)',
        security: [['bearerAuth' => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ['mobile'],
                properties: [new OA\Property(property: 'mobile', type: 'string', example: '09121234567')]
            )
        ),
        responses: [
            new OA\Response(response: 200, description: 'Transferred'),
            new OA\Response(response: 404, description: 'Doctor not found'),
            new OA\Response(response: 409, description: 'Already claimed / target user owns another doctor'),
            new OA\Response(response: 422, description: 'Invalid mobile'),
        ]
    )]
    #[Route('/api/v1/admin/doctors/{uuid}/transfer', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function transfer(string $uuid, Request $request): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($uuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404);
        }

        $data   = json_decode($request->getContent(), true) ?? [];
        $mobile = trim((string) ($data['mobile'] ?? ''));
        if (!preg_match('/^09\d{9}$/', $mobile)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره موبایل نامعتبر است', 422, 'mobile');
        }

        $claim = $this->claimService->transferByAdmin($doctor, $mobile);

        return $this->success([
            'uuid'         => $doctor->getUuid(),
            'owner_status' => $doctor->getOwnerStatus(),
            'user_mobile'  => $mobile,
            'claim'        => ['uuid' => $claim->getUuid()],
        ]);
    }

    #[OA\Get(
        path: '/api/v1/admin/doctor-claims',
        summary: 'Paginated audit list of doctor profile claim requests',
        security: [['bearerAuth' => []]],
        parameters: [
            new OA\Parameter(name: 'status', in: 'query', schema: new OA\Schema(type: 'string', enum: ['pending', 'completed', 'failed'])),
            new OA\Parameter(name: 'page', in: 'query', schema: new OA\Schema(type: 'integer', default: 1)),
            new OA\Parameter(name: 'limit', in: 'query', schema: new OA\Schema(type: 'integer', default: 20)),
        ],
        responses: [new OA\Response(response: 200, description: 'Paginated claim requests')]
    )]
    #[Route('/api/v1/admin/doctor-claims', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function claimsList(Request $request): JsonResponse
    {
        $page   = max(1, (int) $request->query->get('page', 1));
        $limit  = min(50, max(1, (int) $request->query->get('limit', 20)));
        $status = trim((string) $request->query->get('status', ''));

        $qb = $this->claimRepo->createQueryBuilder('c')
            ->orderBy('c.createdAt', 'DESC');

        if (in_array($status, [\App\Doctor\Entity\DoctorClaimRequest::STATUS_PENDING, \App\Doctor\Entity\DoctorClaimRequest::STATUS_COMPLETED, \App\Doctor\Entity\DoctorClaimRequest::STATUS_FAILED], true)) {
            $qb->andWhere('c.status = :status')->setParameter('status', $status);
        }

        $total = (int) (clone $qb)->select('COUNT(c.id)')->getQuery()->getSingleScalarResult();

        $items = $qb->setFirstResult(($page - 1) * $limit)
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();

        return $this->paginated(
            array_map(fn(\App\Doctor\Entity\DoctorClaimRequest $c) => $c->toArray(), $items),
            $total,
            $page,
            $limit
        );
    }
}
