<?php

namespace App\ClinicInvitation\Controller;

use App\Auth\Entity\User;
use App\Shared\Constant\ErrorCodes;
use App\ClinicInvitation\Entity\ClinicDoctorInvitation;
use App\ClinicInvitation\Repository\ClinicDoctorInvitationRepository;
use App\ClinicInvitation\Service\ClinicInvitationService;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
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;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'Clinic Invitations')]
class ClinicInvitationController extends BaseController
{
    public function __construct(
        private readonly ClinicInvitationService          $invitationService,
        private readonly ClinicDoctorInvitationRepository $invRepo,
        private readonly ClinicRepository                 $clinicRepo,
        private readonly DoctorRepository                 $doctorRepo,
    ) {}

    // ── Admin endpoints ──────────────────────────────────────────────────────

    #[Route('/api/v1/admin/clinic/{uuid}/invite-doctor', methods: ['POST'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function inviteDoctor(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->clinicRepo->findByUuid($uuid);
        if (!$clinic) {
            throw new AppException('ERR_NOT_FOUND_001', 'کلینیک یافت نشد', 404);
        }

        $this->assertClinicAccess($clinic, $user);

        $body      = json_decode($request->getContent(), true) ?? [];
        $mobile    = trim($body['mobile'] ?? '');
        $name      = !empty($body['name']) ? trim($body['name']) : null;
        $specialty = !empty($body['specialty']) ? trim($body['specialty']) : null;

        if (!$mobile || !preg_match('/^09\d{9}$/', $mobile)) {
            throw new AppException('ERR_VALIDATION_001', 'شماره موبایل نامعتبر است', 422, 'mobile');
        }

        $inv = $this->invitationService->invite($clinic, $user, $mobile, $name, $specialty);

        return $this->success($inv->toArray(), 201);
    }

    #[Route('/api/v1/admin/clinic/{uuid}/invitations', methods: ['GET'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function listInvitations(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->clinicRepo->findByUuid($uuid);
        if (!$clinic) {
            throw new AppException('ERR_NOT_FOUND_001', 'کلینیک یافت نشد', 404);
        }

        $this->assertClinicAccess($clinic, $user);

        $page  = max(1, (int) $request->query->get('page', 1));
        $limit = min(50, max(1, (int) $request->query->get('limit', 20)));

        $qb = $this->invRepo->createQueryBuilder('i')
            ->leftJoin('i.doctor', 'd')
            ->where('i.clinic = :clinic')
            ->setParameter('clinic', $clinic)
            ->orderBy('i.invitedAt', 'DESC');

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

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

        $data = array_map(fn($inv) => $inv->toArray(), $items);

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

    #[Route('/api/v1/admin/clinic/invitation/{invUuid}/resend', methods: ['POST'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function resendInvitation(string $invUuid, #[CurrentUser] User $user): JsonResponse
    {
        $inv = $this->invRepo->findOneBy(['uuid' => $invUuid]);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $this->assertClinicAccess($inv->getClinic(), $user);
        $this->invitationService->resend($inv);

        return $this->success(['message' => 'پیامک مجدداً ارسال شد']);
    }

    #[Route('/api/v1/admin/clinic/invitation/{invUuid}/status', methods: ['PATCH'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function changeInvitationStatus(string $invUuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $inv = $this->invRepo->findOneBy(['uuid' => $invUuid]);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $this->assertClinicAccess($inv->getClinic(), $user);

        $body   = json_decode($request->getContent(), true) ?? [];
        $status = $body['status'] ?? '';

        $this->invitationService->changeStatus($inv, $status);

        return $this->success(['status' => $inv->getStatus()]);
    }

    #[Route('/api/v1/admin/clinic/invitation/{invUuid}', methods: ['DELETE'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function deleteInvitation(string $invUuid, #[CurrentUser] User $user): JsonResponse
    {
        $inv = $this->invRepo->findOneBy(['uuid' => $invUuid]);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $this->assertClinicAccess($inv->getClinic(), $user);
        $this->invitationService->delete($inv);

        return $this->success(null, 204);
    }

    // ── Doctor-facing endpoints ──────────────────────────────────────────────

    #[Route('/api/v1/doctor/invitations', methods: ['GET'])]
    #[IsGranted('ROLE_DOCTOR')]
    public function myInvitations(#[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUser($user);
        if (!$doctor) {
            throw new AppException('ERR_NOT_FOUND_001', 'پروفایل پزشک یافت نشد', 404);
        }

        $invitations = $this->invRepo->findPendingByDoctor($doctor);

        $data = array_map(function (ClinicDoctorInvitation $inv): array {
            $clinic = $inv->getClinic();
            $arr    = $inv->toArray();
            $arr['clinic'] = [
                'uuid' => $clinic->getUuid(),
                'name' => $clinic->getName(),
                'logo' => $clinic->getClinicLogo(),
            ];
            return $arr;
        }, $invitations);

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

    #[Route('/api/v1/doctor/invitation/{invUuid}/respond', methods: ['POST'])]
    #[IsGranted('ROLE_DOCTOR')]
    public function respondToInvitation(string $invUuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUser($user);
        if (!$doctor) {
            throw new AppException('ERR_NOT_FOUND_001', 'پروفایل پزشک یافت نشد', 404);
        }

        $inv = $this->invRepo->findOneBy(['uuid' => $invUuid]);
        if (!$inv || $inv->getDoctor()?->getId() !== $doctor->getId()) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $body   = json_decode($request->getContent(), true) ?? [];
        $action = $body['action'] ?? '';

        if ($action === 'accept') {
            $this->invitationService->accept($inv);
            return $this->success(['message' => 'دعوتنامه پذیرفته شد']);
        }

        if ($action === 'reject') {
            $this->invitationService->reject($inv);
            return $this->success(['message' => 'دعوتنامه رد شد']);
        }

        throw new AppException('ERR_VALIDATION_001', 'action باید accept یا reject باشد', 422);
    }

    private function assertClinicAccess(\App\Clinic\Entity\Clinic $clinic, User $user): void
    {
        if ($user->hasRole('ROLE_ADMIN')) {
            return;
        }
        if ($user->hasRole('ROLE_CLINIC') && $clinic->getUser()->getId() === $user->getId()) {
            return;
        }
        throw new AppException(ErrorCodes::ERR_ACCESS_DENIED, 'دسترسی ندارید', 403);
    }

    // ── Public endpoints ──────────────────────────────────────────────────────

    #[Route('/api/v1/clinic-invitation/{token}', methods: ['GET'])]
    public function viewInvitation(string $token): JsonResponse
    {
        $inv = $this->invRepo->findByToken($token);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $clinic = $inv->getClinic();

        return $this->success([
            'invitation' => $inv->toArray(),
            'clinic'     => [
                'uuid' => $clinic->getUuid(),
                'name' => $clinic->getName(),
                'logo' => $clinic->getClinicLogo(),
            ],
            'is_usable' => $inv->isUsable(),
        ]);
    }

    #[Route('/api/v1/clinic-invitation/{token}/accept', methods: ['POST'])]
    public function acceptInvitation(string $token): JsonResponse
    {
        $inv = $this->invRepo->findByToken($token);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $this->invitationService->accept($inv);

        return $this->success(['message' => 'دعوتنامه پذیرفته شد']);
    }

    #[Route('/api/v1/clinic-invitation/{token}/reject', methods: ['POST'])]
    public function rejectInvitation(string $token): JsonResponse
    {
        $inv = $this->invRepo->findByToken($token);
        if (!$inv) {
            throw new AppException('ERR_NOT_FOUND_001', 'دعوتنامه یافت نشد', 404);
        }

        $this->invitationService->reject($inv);

        return $this->success(['message' => 'دعوتنامه رد شد']);
    }
}
