<?php

namespace App\Auth\Controller;

use App\Auth\Entity\PreRegistration;
use App\Auth\Entity\User;
use App\Auth\Repository\PreRegistrationRepository;
use App\Auth\Repository\UserRepository;
use App\Clinic\Entity\Clinic;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Entity\Doctor;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Captcha\CaptchaGuard;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Sms\Service\SmsService;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
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\IsGranted;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'Auth')]
class PreRegistrationController extends BaseController
{
    public function __construct(
        private readonly EntityManagerInterface      $em,
        private readonly PreRegistrationRepository   $preRegRepo,
        private readonly UserRepository              $userRepo,
        private readonly DoctorRepository            $doctorRepo,
        private readonly ClinicRepository            $clinicRepo,
        private readonly UserPasswordHasherInterface $hasher,
        private readonly SmsService                  $sms,
        private readonly LoggerInterface             $logger,
        private readonly string                      $appUrl,
        private readonly CaptchaGuard                $captcha,
    ) {}

    #[Route('/api/v1/pre-registration', methods: ['POST'])]
    public function submit(Request $request): JsonResponse
    {
        $this->captcha->assertValid($request);

        $data   = json_decode($request->getContent(), true) ?? [];
        $type   = trim($data['type']   ?? '');
        $name   = trim($data['name']   ?? '');
        $mobile = trim($data['mobile'] ?? '');
        $info   = trim($data['info']   ?? '') ?: null;

        $validTypes = [
            PreRegistration::TYPE_INDEPENDENT_DOCTOR,
            PreRegistration::TYPE_DOCTOR_WITH_CLINIC,
            PreRegistration::TYPE_CLINIC_MANAGER,
        ];

        if (!in_array($type, $validTypes, true)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نوع حساب معتبر نیست', 422);
        }
        if (mb_strlen($mobile) < 10 || mb_strlen($mobile) > 15) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'شماره موبایل معتبر نیست', 422);
        }
        if (mb_strlen($name) < 2) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نام الزامی است', 422);
        }

        if ($this->preRegRepo->hasPendingForMobile($mobile)) {
            return $this->error(ErrorCodes::DUPLICATE_REQUEST, 'درخواست ثبت‌نام شما در حال بررسی است', 409);
        }

        $preReg = new PreRegistration($type, $name, $mobile, $info);
        $this->em->persist($preReg);
        $this->em->flush();

        return $this->success(['uuid' => $preReg->getUuid(), 'status' => $preReg->getStatus()], 201);
    }

    #[Route('/api/v1/admin/pre-registrations', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function list(Request $request): JsonResponse
    {
        $page   = max(1, (int) $request->query->get('page', 1));
        $limit  = min(50, max(1, (int) $request->query->get('limit', 20)));
        $status = $request->query->get('status', 'pending');

        $qb = $this->em->createQueryBuilder()
            ->select('p.uuid, p.type, p.name, p.mobile, p.info, p.status, p.adminNote AS admin_note, p.createdAt AS created_at')
            ->from(PreRegistration::class, 'p');

        if ($status !== 'all') {
            $qb->where('p.status = :status')->setParameter('status', $status);
        }

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

        $items = $qb
            ->orderBy('p.createdAt', 'DESC')
            ->setFirstResult(($page - 1) * $limit)
            ->setMaxResults($limit)
            ->getQuery()
            ->getArrayResult();

        return $this->paginated($items, (int) $total, $page, $limit);
    }

    #[Route('/api/v1/admin/pre-registrations/{uuid}/approve', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function approve(string $uuid): JsonResponse
    {
        $preReg = $this->preRegRepo->findOneBy(['uuid' => $uuid]);
        if (!$preReg) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }
        if (!$preReg->isPending()) {
            return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این درخواست قبلاً پردازش شده است', 409);
        }

        $password = bin2hex(random_bytes(4));

        $user = $this->userRepo->findOneBy(['mobileNumber' => $preReg->getMobile()]);
        if (!$user) {
            $user = new User($preReg->getMobile());
        }
        $user->setPasswordHash($this->hasher->hashPassword($user, $password));
        $user->setRealName($preReg->getName());

        $this->em->persist($user);

        $type = $preReg->getType();

        $doctor = null;
        if ($type === PreRegistration::TYPE_INDEPENDENT_DOCTOR || $type === PreRegistration::TYPE_DOCTOR_WITH_CLINIC) {
            $user->addRole('ROLE_DOCTOR');
            $doctor = $this->doctorRepo->findOneBy(['user' => $user]);
            if (!$doctor) {
                $doctor = new Doctor($user, $preReg->getName());
                $doctor->setMobileNumber($preReg->getMobile());
                $this->em->persist($doctor);
            }
        }

        if ($type === PreRegistration::TYPE_DOCTOR_WITH_CLINIC || $type === PreRegistration::TYPE_CLINIC_MANAGER) {
            $user->addRole('ROLE_CLINIC');
            if (!$this->clinicRepo->findOneBy(['user' => $user])) {
                $clinic = new Clinic($user);
                $clinic->setName($preReg->getName());
                $clinic->setTelephone($preReg->getMobile());
                $clinic->setNotificationMobile($preReg->getMobile());
                if ($doctor !== null) {
                    $clinic->getDoctors()->add($doctor);
                }
                $this->em->persist($clinic);
            }
        }

        $preReg->approve();
        $this->em->flush();

        try {
            $this->sms->dispatchTemplate(
                \App\Sms\Entity\SmsLog::TAG_PRE_REGISTRATION,
                $preReg->getMobile(),
                [
                    'username' => $preReg->getMobile(),
                    'password' => $password,
                    'link'     => rtrim($this->appUrl, '/') . '/admin',
                ],
            );
        } catch (\Throwable $e) {
            $this->logger->warning('PreRegistration SMS failed', ['uuid' => $uuid, 'error' => $e->getMessage()]);
        }

        return $this->success(['message' => 'تأیید شد و اطلاعات ورود ارسال گردید']);
    }

    #[Route('/api/v1/admin/pre-registrations/{uuid}/reject', methods: ['POST'])]
    #[IsGranted('ROLE_ADMIN')]
    public function reject(string $uuid, Request $request): JsonResponse
    {
        $preReg = $this->preRegRepo->findOneBy(['uuid' => $uuid]);
        if (!$preReg) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'درخواست یافت نشد', 404);
        }
        if (!$preReg->isPending()) {
            return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این درخواست قبلاً پردازش شده است', 409);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        $preReg->reject($data['note'] ?? null);
        $this->em->flush();

        return $this->success(['message' => 'درخواست رد شد']);
    }
}
