<?php

namespace App\Tag\Controller;

use App\Auth\Entity\User;
use App\Auth\Repository\UserActiveContextRepository;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Secretary\Security\SecretaryAccessChecker;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
use App\Tag\Entity\TenantTag;
use App\Tag\Repository\TenantTagRepository;
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;

/**
 * Per-tenant (doctor/clinic) tag management. Every tag is scoped to the caller's
 * resolved entity; a tenant can only see and mutate its own tags.
 */
#[OA\Tag(name: 'Tenant Tags')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class TenantTagController extends BaseController
{
    private const HEX = '/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/';

    public function __construct(
        private readonly TenantTagRepository         $tagRepo,
        private readonly DoctorRepository            $doctorRepo,
        private readonly ClinicRepository            $clinicRepo,
        private readonly UserActiveContextRepository $contextRepo,
        private readonly SecretaryAccessChecker      $secretaryAccess,
        private readonly \App\Clinic\Security\ClinicDoctorAccessChecker $clinicDoctorAccess,
    ) {}

    /** تگ‌ها ابزار پروندهٔ بیمار هم هستند؛ مشاهده با tags.view یا patients.view مجاز است. */
    private function guardTagView(User $user): void
    {
        if (!$this->secretaryAccess->canOrNonSecretary($user, 'tags', 'view')
            && !$this->secretaryAccess->canOrNonSecretary($user, 'patients', 'view')) {
            throw new AppException(ErrorCodes::ERR_FORBIDDEN_001, null, 403);
        }
    }

    #[Route('/api/v1/tenant-tags', methods: ['GET'])]
    public function list(#[CurrentUser] User $user): JsonResponse
    {
        $this->guardTagView($user);
        [$type, $id] = $this->resolveEntity($user);
        if ($id === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        return $this->success(array_map(
            fn(TenantTag $t) => $t->toArray(),
            $this->tagRepo->findByEntity($type, $id)
        ));
    }

    #[Route('/api/v1/tenant-tag', methods: ['POST'])]
    public function create(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $this->secretaryAccess->denyUnlessGranted($user, 'tags', 'create');
        $this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'create');
        [$type, $id] = $this->resolveEntity($user);
        if ($id === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'پروفایل یافت نشد', 403);
        }

        $data  = json_decode($request->getContent(), true) ?? [];
        $name  = trim($data['name'] ?? '');
        $color = trim($data['color'] ?? '#5559CE');

        if ($name === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نام برچسب الزامی است', 422, 'name');
        }
        if (!preg_match(self::HEX, $color)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'رنگ نامعتبر است', 422, 'color');
        }

        $tag = new TenantTag($type, $id, $name, $color);
        if (array_key_exists('active', $data)) {
            $tag->setActive((bool) $data['active']);
        }
        $this->tagRepo->save($tag);

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

    #[Route('/api/v1/tenant-tag/{uuid}', methods: ['PATCH'])]
    public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $this->secretaryAccess->denyUnlessGranted($user, 'tags', 'update');
        $this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'update');
        $tag = $this->ownedTag($uuid, $user);
        if ($tag === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'برچسب یافت نشد', 404);
        }

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

        if (isset($data['name'])) {
            $name = trim($data['name']);
            if ($name === '') {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نام برچسب الزامی است', 422, 'name');
            }
            $tag->setName($name);
        }
        if (isset($data['color'])) {
            if (!preg_match(self::HEX, trim($data['color']))) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'رنگ نامعتبر است', 422, 'color');
            }
            $tag->setColor(trim($data['color']));
        }
        if (isset($data['active'])) {
            $tag->setActive((bool) $data['active']);
        }

        $this->tagRepo->save($tag);

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

    #[Route('/api/v1/tenant-tag/{uuid}', methods: ['DELETE'])]
    public function delete(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $this->secretaryAccess->denyUnlessGranted($user, 'tags', 'delete');
        $this->clinicDoctorAccess->denyUnlessGranted($user, 'tags', 'delete');
        $tag = $this->ownedTag($uuid, $user);
        if ($tag === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'برچسب یافت نشد', 404);
        }

        $this->tagRepo->remove($tag);

        return $this->success(['message' => 'برچسب حذف شد']);
    }

    // ── Helpers ─────────────────────────────────────────────────────────────

    /** The tag only if it belongs to the caller's entity, else null. */
    private function ownedTag(string $uuid, User $user): ?TenantTag
    {
        [$type, $id] = $this->resolveEntity($user);
        $tag = $this->tagRepo->findByUuid($uuid);
        if ($tag === null || $id === null || $tag->getEntityType() !== $type || $tag->getEntityId() !== $id) {
            return null;
        }
        return $tag;
    }

    /**
     * عمداً از EntityContextResolver استفاده نمی‌کند: اینجا نقشِ پزشک بی‌قید اول
     * بررسی می‌شود، پس پزشکِ عضو حتی در محیط کلینیک هم برچسب‌های شخصی خودش را
     * می‌بیند. رزولور مشترک محیط فعال را مقدم می‌داند و انتقال به آن، دادهٔ
     * نمایش‌داده‌شده را عوض می‌کند — به فاز نشانه‌گذاری tenant موکول شد.
     *
     * @return array{0: string, 1: int|null} [entityType, entityId]
     */
    private function resolveEntity(User $user): array
    {
        if ($user->hasRole('ROLE_DOCTOR')) {
            $memberClinicId = $this->clinicDoctorAccess->memberClinicId($user);
            if ($memberClinicId !== null) {
                return ['clinic', $memberClinicId];
            }
            $doctor = $this->doctorRepo->findByUser($user);
            return ['doctor', $doctor?->getId()];
        }
        if ($user->hasRole('ROLE_CLINIC')) {
            $clinic = $this->clinicRepo->findByUser($user);
            return ['clinic', $clinic?->getId()];
        }
        if ($user->hasRole('ROLE_SECRETARY')) {
            $dbUuid = $this->contextRepo->findByUser($user)?->getDbUuid();
            if ($dbUuid !== null) {
                $clinic = $this->clinicRepo->findByUuid($dbUuid);
                if ($clinic !== null) {
                    return ['clinic', $clinic->getId()];
                }
                $doctor = $this->doctorRepo->findByUuid($dbUuid);
                if ($doctor !== null) {
                    return ['doctor', $doctor->getId()];
                }
            }
        }
        return ['unknown', null];
    }
}
