<?php

declare(strict_types=1);

namespace App\Module\Absence\Domain\Entity;

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\Module\Absence\Domain\Enum\AbsenceStatus;
use App\Module\Absence\Domain\Enum\AbsenceType;
use App\Module\Absence\Domain\Enum\HalfDay;
use App\Module\Absence\Infrastructure\ApiPlatform\State\AbsenceCancelProcessor;
use App\Module\Absence\Infrastructure\ApiPlatform\State\AbsenceRequestProcessor;
use App\Module\Absence\Infrastructure\ApiPlatform\State\AbsenceRequestProvider;
use App\Module\Absence\Infrastructure\ApiPlatform\State\AbsenceReviewProcessor;
use App\Module\Absence\Infrastructure\Doctrine\DoctrineAbsenceRequestRepository;
use App\Shared\Domain\Contract\UserInterface;
use DateTimeImmutable;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;

#[ApiResource(
    operations: [
        new GetCollection(
            paginationEnabled: false,
            security: "is_granted('ROLE_USER')",
            provider: AbsenceRequestProvider::class,
        ),
        new Get(
            security: "is_granted('ROLE_USER')",
            provider: AbsenceRequestProvider::class,
        ),
        new Post(
            security: "is_granted('ROLE_USER')",
            processor: AbsenceRequestProcessor::class,
        ),
        new Patch(
            uriTemplate: '/absence_requests/{id}/approve',
            security: "is_granted('ROLE_ADMIN')",
            processor: AbsenceReviewProcessor::class,
            provider: AbsenceRequestProvider::class,
        ),
        new Patch(
            uriTemplate: '/absence_requests/{id}/reject',
            security: "is_granted('ROLE_ADMIN')",
            processor: AbsenceReviewProcessor::class,
            provider: AbsenceRequestProvider::class,
        ),
        new Patch(
            uriTemplate: '/absence_requests/{id}/cancel',
            security: "is_granted('ROLE_USER')",
            processor: AbsenceCancelProcessor::class,
            provider: AbsenceRequestProvider::class,
        ),
        new Delete(security: "is_granted('ROLE_ADMIN')"),
    ],
    normalizationContext: ['groups' => ['absence_request:read']],
    denormalizationContext: ['groups' => ['absence_request:write']],
    order: ['createdAt' => 'DESC'],
)]
#[ORM\Entity(repositoryClass: DoctrineAbsenceRequestRepository::class)]
#[ORM\Table(name: 'absence_request')]
class AbsenceRequest
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    #[Groups(['absence_request:read'])]
    private ?int $id = null;

    #[ORM\ManyToOne(targetEntity: UserInterface::class)]
    #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
    #[Groups(['absence_request:read'])]
    private ?UserInterface $user = null;

    #[ORM\Column(type: Types::STRING, length: 32, enumType: AbsenceType::class)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    #[Assert\NotNull]
    private ?AbsenceType $type = null;

    #[ORM\Column(type: Types::DATE_IMMUTABLE)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    #[Assert\NotNull]
    private ?DateTimeImmutable $startDate = null;

    #[ORM\Column(type: Types::DATE_IMMUTABLE)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    #[Assert\NotNull]
    private ?DateTimeImmutable $endDate = null;

    #[ORM\Column(type: Types::STRING, length: 16, nullable: true, enumType: HalfDay::class)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    private ?HalfDay $startHalfDay = null;

    #[ORM\Column(type: Types::STRING, length: 16, nullable: true, enumType: HalfDay::class)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    private ?HalfDay $endHalfDay = null;

    /** Number of deducted days, computed server-side at creation. */
    #[ORM\Column(type: Types::FLOAT)]
    #[Groups(['absence_request:read'])]
    private float $countedDays = 0.0;

    #[ORM\Column(type: Types::TEXT, nullable: true)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    private ?string $reason = null;

    #[ORM\Column(length: 255, nullable: true)]
    #[Groups(['absence_request:read'])]
    private ?string $justificationFileName = null;

    #[ORM\Column(type: Types::STRING, length: 16, enumType: AbsenceStatus::class)]
    #[Groups(['absence_request:read'])]
    private AbsenceStatus $status = AbsenceStatus::Pending;

    #[ORM\Column(type: Types::TEXT, nullable: true)]
    #[Groups(['absence_request:read', 'absence_request:write'])]
    private ?string $rejectionReason = null;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
    #[Groups(['absence_request:read'])]
    private ?DateTimeImmutable $createdAt = null;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
    #[Groups(['absence_request:read'])]
    private ?DateTimeImmutable $reviewedAt = null;

    #[ORM\ManyToOne(targetEntity: UserInterface::class)]
    #[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
    #[Groups(['absence_request:read'])]
    private ?UserInterface $reviewedBy = null;

    #[Groups(['absence_request:read'])]
    public function getLabel(): ?string
    {
        return $this->type?->label();
    }

    #[Groups(['absence_request:read'])]
    public function getJustificationUrl(): ?string
    {
        if (null === $this->justificationFileName) {
            return null;
        }

        return '/api/absence_requests/'.$this->id.'/justificatif';
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getUser(): ?UserInterface
    {
        return $this->user;
    }

    public function setUser(?UserInterface $user): static
    {
        $this->user = $user;

        return $this;
    }

    public function getType(): ?AbsenceType
    {
        return $this->type;
    }

    public function setType(?AbsenceType $type): static
    {
        $this->type = $type;

        return $this;
    }

    public function getStartDate(): ?DateTimeImmutable
    {
        return $this->startDate;
    }

    public function setStartDate(?DateTimeImmutable $startDate): static
    {
        $this->startDate = $startDate;

        return $this;
    }

    public function getEndDate(): ?DateTimeImmutable
    {
        return $this->endDate;
    }

    public function setEndDate(?DateTimeImmutable $endDate): static
    {
        $this->endDate = $endDate;

        return $this;
    }

    public function getStartHalfDay(): ?HalfDay
    {
        return $this->startHalfDay;
    }

    public function setStartHalfDay(?HalfDay $startHalfDay): static
    {
        $this->startHalfDay = $startHalfDay;

        return $this;
    }

    public function getEndHalfDay(): ?HalfDay
    {
        return $this->endHalfDay;
    }

    public function setEndHalfDay(?HalfDay $endHalfDay): static
    {
        $this->endHalfDay = $endHalfDay;

        return $this;
    }

    public function getCountedDays(): float
    {
        return $this->countedDays;
    }

    public function setCountedDays(float $countedDays): static
    {
        $this->countedDays = $countedDays;

        return $this;
    }

    public function getReason(): ?string
    {
        return $this->reason;
    }

    public function setReason(?string $reason): static
    {
        $this->reason = $reason;

        return $this;
    }

    public function getJustificationFileName(): ?string
    {
        return $this->justificationFileName;
    }

    public function setJustificationFileName(?string $justificationFileName): static
    {
        $this->justificationFileName = $justificationFileName;

        return $this;
    }

    public function getStatus(): AbsenceStatus
    {
        return $this->status;
    }

    public function setStatus(AbsenceStatus $status): static
    {
        $this->status = $status;

        return $this;
    }

    public function getRejectionReason(): ?string
    {
        return $this->rejectionReason;
    }

    public function setRejectionReason(?string $rejectionReason): static
    {
        $this->rejectionReason = $rejectionReason;

        return $this;
    }

    public function getCreatedAt(): ?DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function setCreatedAt(DateTimeImmutable $createdAt): static
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getReviewedAt(): ?DateTimeImmutable
    {
        return $this->reviewedAt;
    }

    public function setReviewedAt(?DateTimeImmutable $reviewedAt): static
    {
        $this->reviewedAt = $reviewedAt;

        return $this;
    }

    public function getReviewedBy(): ?UserInterface
    {
        return $this->reviewedBy;
    }

    public function setReviewedBy(?UserInterface $reviewedBy): static
    {
        $this->reviewedBy = $reviewedBy;

        return $this;
    }
}
