<?php

namespace App\Resource\Service;

use App\Auth\Entity\User;
use App\Resource\Entity\ClinicResource;
use App\Resource\Entity\ResourceSkill;
use App\Resource\Entity\Skill;
use App\Resource\Repository\ResourceSkillRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use Doctrine\ORM\EntityManagerInterface;

final class SkillAssignmentService
{
    public function __construct(
        private readonly ResourceSkillRepository $resourceSkills,
        private readonly ResourceContext         $context,
        private readonly EntityManagerInterface  $em,
    ) {}

    /**
     * جایگزینی **کامل** مهارت‌های یک منبع: مهارتی که در بدنه نیست، برداشته می‌شود.
     *
     * مثل ساعت کاری شعبه، اعتبارسنجی همهٔ ورودی پیش از هر حذفی انجام می‌شود تا یک
     * ورودی نامعتبر در انتهای فهرست، مهارت‌های درستِ قبلی را پاک نکند و بعد ۴۲۲ بدهد.
     *
     * @param array<int, mixed> $rows
     * @return ResourceSkill[]
     */
    public function replace(User $user, ClinicResource $resource, array $rows): array
    {
        $validated = $this->validate($user, $rows);

        $this->resourceSkills->deleteForResource($resource);
        $resource->getSkills()->clear();

        $created = [];
        foreach ($validated as $row) {
            $assignment = new ResourceSkill($resource, $row['skill'], $row['level']);
            $this->em->persist($assignment);
            $resource->getSkills()->add($assignment);
            $created[] = $assignment;
        }

        $this->em->flush();

        return $created;
    }

    /**
     * @param array<int, mixed> $rows
     * @return list<array{skill: Skill, level: int}>
     */
    private function validate(User $user, array $rows): array
    {
        $seen = [];
        $out  = [];

        foreach ($rows as $row) {
            if (!is_array($row) || !is_string($row['skill_uuid'] ?? null)) {
                throw new AppException(ErrorCodes::ERR_VALIDATION_002, 'فیلد skill_uuid الزامی است', 422, 'skill_uuid');
            }

            $uuid = $row['skill_uuid'];

            if (isset($seen[$uuid])) {
                throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'یک مهارت دو بار فرستاده شده است', 422, 'skill_uuid');
            }
            $seen[$uuid] = true;

            // مهارت محیط دیگر → ۴۰۴، پیش از هر تغییری در دیتابیس.
            $skill = $this->context->skill($user, $uuid);
            $level = $row['level'] ?? ResourceSkill::MIN_LEVEL;

            if (!is_numeric($level) || (int) $level < ResourceSkill::MIN_LEVEL || (int) $level > ResourceSkill::MAX_LEVEL) {
                throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'سطح مهارت باید بین ۱ تا ۵ باشد', 422, 'level');
            }

            $out[] = ['skill' => $skill, 'level' => (int) $level];
        }

        return $out;
    }

    /**
     * مهارتی که روی منبعی نشسته حذف نمی‌شود — اول باید از منابع برداشته شود، وگرنه
     * `ON DELETE RESTRICT` خطای خام دیتابیس می‌داد.
     */
    public function deleteSkill(Skill $skill): void
    {
        $inUse = $this->resourceSkills->countForSkill($skill);

        if ($inUse > 0) {
            throw new AppException(
                ErrorCodes::ERR_VALIDATION_001,
                sprintf('این مهارت به %d منبع داده شده است؛ اول از آن‌ها برداشته شود', $inUse),
                422,
            );
        }

        $this->em->remove($skill);
        $this->em->flush();
    }
}
