<?php

namespace App\Clinic\Controller;

use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Clinic\Entity\ClinicDoctorPermission;
use App\Clinic\Repository\ClinicDoctorPermissionRepository;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
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: 'Clinic Doctor Permissions')]
class ClinicDoctorPermissionController extends BaseController
{
    public function __construct(
        private readonly ClinicRepository                 $clinicRepo,
        private readonly DoctorRepository                 $doctorRepo,
        private readonly ClinicDoctorPermissionRepository $permRepo,
        private readonly EntityManagerInterface           $em,
        private readonly \App\Secretary\Security\SecretaryAccessChecker $secretaryAccess,
    ) {}

    #[Route('/api/v1/admin/clinic/{clinicUuid}/doctor-permissions', methods: ['GET'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function listPermissions(string $clinicUuid, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->resolveClinic($clinicUuid, $user, 'view');

        $data = array_map(
            fn(ClinicDoctorPermission $p) => $p->toArray(),
            $this->permRepo->findByClinic($clinic),
        );

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

    #[Route('/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}/permissions', methods: ['GET'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function showPermissions(string $clinicUuid, string $doctorUuid, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->resolveClinic($clinicUuid, $user, 'view');
        $doctor = $this->resolveMember($clinic, $doctorUuid);

        return $this->success($this->permRepo->getOrCreate($clinic, $doctor)->toArray());
    }

    #[Route('/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}/permissions', methods: ['PATCH'])]
    #[IsGranted('IS_AUTHENTICATED_FULLY')]
    public function updatePermissions(string $clinicUuid, string $doctorUuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->resolveClinic($clinicUuid, $user, 'update');
        $doctor = $this->resolveMember($clinic, $doctorUuid);
        $perm   = $this->permRepo->getOrCreate($clinic, $doctor);

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

        if (array_key_exists('permissions', $body)) {
            if (!is_array($body['permissions'])) {
                throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'permissions باید آبجکت باشد', 422, 'permissions');
            }
            $perm->mergePermissions($body['permissions']);
        }

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

        $this->em->flush();

        return $this->success($perm->toArray());
    }

    private function resolveClinic(string $clinicUuid, User $user, string $action): Clinic
    {
        $clinic = $this->clinicRepo->findByUuid($clinicUuid);
        if ($clinic === null) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'کلینیک یافت نشد', 404);
        }

        $isOwner = $clinic->getUser()->getId() === $user->getId();
        // منشیِ همان کلینیک با توگلِ clinic_doctors می‌تواند مدیریت پزشکان را انجام دهد.
        $isSecretary = $this->secretaryAccess->canForClinic($user, $clinic, 'clinic_doctors', $action);
        if (!$user->hasRole('ROLE_ADMIN') && !$isOwner && !$isSecretary) {
            throw new AppException(ErrorCodes::ERR_ACCESS_DENIED, 'دسترسی ندارید', 403);
        }

        return $clinic;
    }

    private function resolveMember(Clinic $clinic, string $doctorUuid): \App\Doctor\Entity\Doctor
    {
        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null || !$clinic->hasDoctor($doctor)) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'این پزشک به کلینیک متصل نیست', 404);
        }

        return $doctor;
    }
}
