<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Api\Resource;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Administration\Application\Query\GetUsers\UserDto;
use App\Administration\Domain\Model\User\Role;
use App\Administration\Domain\Model\User\User;
use App\Administration\Infrastructure\Api\Processor\BlockUserProcessor;
use App\Administration\Infrastructure\Api\Processor\InviteUserProcessor;
use App\Administration\Infrastructure\Api\Processor\ResendInvitationProcessor;
use App\Administration\Infrastructure\Api\Processor\UnblockUserProcessor;
use App\Administration\Infrastructure\Api\Processor\UpdateUserRolesProcessor;
use App\Administration\Infrastructure\Api\Provider\UserCollectionProvider;
use App\Administration\Infrastructure\Api\Provider\UserItemProvider;
use DateTimeImmutable;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * API Resource pour la gestion des utilisateurs.
 *
 * @see Story 2.5 - Création Comptes Utilisateurs
 * @see Story 2.6 - Attribution des Rôles
 */
#[ApiResource(
    shortName: 'User',
    operations: [
        new GetCollection(
            uriTemplate: '/users',
            provider: UserCollectionProvider::class,
            name: 'get_users',
        ),
        new Get(
            uriTemplate: '/users/{id}',
            provider: UserItemProvider::class,
            name: 'get_user',
        ),
        new Post(
            uriTemplate: '/users',
            processor: InviteUserProcessor::class,
            validationContext: ['groups' => ['Default', 'create']],
            name: 'invite_user',
        ),
        new Post(
            uriTemplate: '/users/{id}/resend-invitation',
            processor: ResendInvitationProcessor::class,
            name: 'resend_invitation',
        ),
        new Post(
            uriTemplate: '/users/{id}/block',
            processor: BlockUserProcessor::class,
            validationContext: ['groups' => ['Default', 'block']],
            name: 'block_user',
        ),
        new Post(
            uriTemplate: '/users/{id}/unblock',
            processor: UnblockUserProcessor::class,
            name: 'unblock_user',
        ),
        new Put(
            uriTemplate: '/users/{id}/roles',
            read: false,
            processor: UpdateUserRolesProcessor::class,
            validationContext: ['groups' => ['Default', 'roles']],
            name: 'update_user_roles',
        ),
    ],
)]
final class UserResource
{
    #[ApiProperty(identifier: true)]
    public ?string $id = null;

    #[Assert\NotBlank(message: 'L\'email est requis.', groups: ['create'])]
    #[Assert\Email(message: 'L\'email n\'est pas valide.')]
    public ?string $email = null;

    #[Assert\NotBlank(message: 'Le rôle est requis.', groups: ['create'])]
    public ?string $role = null;

    public ?string $roleLabel = null;

    /** @var string[]|null */
    #[Assert\NotBlank(message: 'Les rôles sont requis.', groups: ['roles'])]
    #[Assert\Count(min: 1, minMessage: 'Au moins un rôle est requis.', groups: ['roles'])]
    public ?array $roles = null;

    #[Assert\NotBlank(message: 'Le prénom est requis.', groups: ['create'])]
    public ?string $firstName = null;

    #[Assert\NotBlank(message: 'Le nom est requis.', groups: ['create'])]
    public ?string $lastName = null;

    public ?string $statut = null;

    public ?DateTimeImmutable $createdAt = null;

    public ?DateTimeImmutable $invitedAt = null;

    public ?DateTimeImmutable $activatedAt = null;

    public ?DateTimeImmutable $blockedAt = null;

    public ?string $blockedReason = null;

    #[ApiProperty(readable: true, writable: false)]
    public ?bool $invitationExpiree = null;

    #[Assert\NotBlank(message: 'La raison de blocage est requise.', groups: ['block'])]
    public ?string $reason = null;

    public static function fromDomain(User $user, ?DateTimeImmutable $now = null): self
    {
        $resource = new self();
        $resource->id = (string) $user->id;
        $resource->email = (string) $user->email;
        $resource->role = $user->role->value;
        $resource->roleLabel = $user->role->label();
        $resource->roles = array_map(
            static fn (Role $r) => $r->value,
            $user->roles,
        );
        $resource->firstName = $user->firstName;
        $resource->lastName = $user->lastName;
        $resource->statut = $user->statut->value;
        $resource->createdAt = $user->createdAt;
        $resource->invitedAt = $user->invitedAt;
        $resource->activatedAt = $user->activatedAt;
        $resource->blockedAt = $user->blockedAt;
        $resource->blockedReason = $user->blockedReason;
        $resource->invitationExpiree = $now !== null ? $user->estInvitationExpiree($now) : false;

        return $resource;
    }

    public static function fromDto(UserDto $dto): self
    {
        $resource = new self();
        $resource->id = $dto->id;
        $resource->email = $dto->email;
        $resource->role = $dto->role;
        $resource->roleLabel = $dto->roleLabel;
        $resource->roles = $dto->roles;
        $resource->firstName = $dto->firstName;
        $resource->lastName = $dto->lastName;
        $resource->statut = $dto->statut;
        $resource->createdAt = $dto->createdAt;
        $resource->invitedAt = $dto->invitedAt;
        $resource->activatedAt = $dto->activatedAt;
        $resource->blockedAt = $dto->blockedAt;
        $resource->blockedReason = $dto->blockedReason;
        $resource->invitationExpiree = $dto->invitationExpiree;

        return $resource;
    }
}
