<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Api\Resource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Administration\Application\Query\GetSubjects\SubjectDto;
use App\Administration\Domain\Model\Subject\Subject;
use App\Administration\Infrastructure\Api\Processor\CreateSubjectProcessor;
use App\Administration\Infrastructure\Api\Processor\DeleteSubjectProcessor;
use App\Administration\Infrastructure\Api\Processor\UpdateSubjectProcessor;
use App\Administration\Infrastructure\Api\Provider\SubjectCollectionProvider;
use App\Administration\Infrastructure\Api\Provider\SubjectItemProvider;
use DateTimeImmutable;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * API Resource pour la gestion des matières.
 *
 * @see Story 2.2 - Création et Gestion des Matières
 * @see FR74 - Structurer l'offre pédagogique
 */
#[ApiResource(
    shortName: 'Subject',
    operations: [
        new GetCollection(
            uriTemplate: '/subjects',
            provider: SubjectCollectionProvider::class,
            name: 'get_subjects',
        ),
        new Get(
            uriTemplate: '/subjects/{id}',
            provider: SubjectItemProvider::class,
            name: 'get_subject',
        ),
        new Post(
            uriTemplate: '/subjects',
            processor: CreateSubjectProcessor::class,
            validationContext: ['groups' => ['Default', 'create']],
            name: 'create_subject',
        ),
        new Patch(
            uriTemplate: '/subjects/{id}',
            provider: SubjectItemProvider::class,
            processor: UpdateSubjectProcessor::class,
            validationContext: ['groups' => ['Default', 'update']],
            name: 'update_subject',
        ),
        new Delete(
            uriTemplate: '/subjects/{id}',
            provider: SubjectItemProvider::class,
            processor: DeleteSubjectProcessor::class,
            name: 'delete_subject',
        ),
    ],
)]
final class SubjectResource
{
    #[ApiProperty(identifier: true)]
    public ?string $id = null;

    #[Assert\NotBlank(message: 'Le nom de la matière est requis.', groups: ['create'])]
    #[Assert\Length(
        min: 2,
        max: 100,
        minMessage: 'Le nom de la matière doit contenir au moins {{ limit }} caractères.',
        maxMessage: 'Le nom de la matière ne peut pas dépasser {{ limit }} caractères.',
    )]
    public ?string $name = null;

    #[Assert\NotBlank(message: 'Le code de la matière est requis.', groups: ['create'])]
    #[Assert\Regex(
        pattern: '/^[A-Za-z0-9]{2,10}$/',
        message: 'Le code doit contenir entre 2 et 10 caractères alphanumériques.',
    )]
    public ?string $code = null;

    #[Assert\Regex(
        pattern: '/^#[0-9A-Fa-f]{6}$/',
        message: 'La couleur doit être au format hexadécimal #RRGGBB.',
    )]
    public ?string $color = null;

    public ?string $description = null;

    public ?string $status = null;

    public ?DateTimeImmutable $createdAt = null;

    public ?DateTimeImmutable $updatedAt = null;

    /**
     * Statistiques : nombre d'enseignants associés à cette matière.
     * Disponible uniquement dans GetCollection.
     */
    #[ApiProperty(readable: true, writable: false)]
    public ?int $teacherCount = null;

    /**
     * Statistiques : nombre de classes associées à cette matière.
     * Disponible uniquement dans GetCollection.
     */
    #[ApiProperty(readable: true, writable: false)]
    public ?int $classCount = null;

    /**
     * Permet de supprimer explicitement la couleur lors d'un PATCH.
     * Si true, la couleur sera mise à null même si color n'est pas fourni.
     */
    #[ApiProperty(readable: false)]
    public ?bool $clearColor = null;

    /**
     * Permet de supprimer explicitement la description lors d'un PATCH.
     * Si true, la description sera mise à null même si description n'est pas fourni.
     */
    #[ApiProperty(readable: false)]
    public ?bool $clearDescription = null;

    /**
     * Indique si la matière a des notes associées (pour avertissement avant suppression).
     */
    #[ApiProperty(readable: true, writable: false)]
    public ?bool $hasGrades = null;

    /**
     * Crée un SubjectResource à partir du domain model.
     */
    public static function fromDomain(Subject $subject): self
    {
        $resource = new self();
        $resource->id = (string) $subject->id;
        $resource->name = (string) $subject->name;
        $resource->code = (string) $subject->code;
        $resource->color = $subject->color !== null ? (string) $subject->color : null;
        $resource->description = $subject->description;
        $resource->status = $subject->status->value;
        $resource->createdAt = $subject->createdAt;
        $resource->updatedAt = $subject->updatedAt;

        return $resource;
    }

    /**
     * Crée un SubjectResource à partir d'un DTO de query.
     */
    public static function fromDto(SubjectDto $dto): self
    {
        $resource = new self();
        $resource->id = $dto->id;
        $resource->name = $dto->name;
        $resource->code = $dto->code;
        $resource->color = $dto->color;
        $resource->description = $dto->description;
        $resource->status = $dto->status;
        $resource->createdAt = $dto->createdAt;
        $resource->updatedAt = $dto->updatedAt;
        $resource->teacherCount = $dto->teacherCount;
        $resource->classCount = $dto->classCount;

        return $resource;
    }
}
