<?php

declare(strict_types=1);

namespace App\Scolarite\Domain\Model\Schedule;

use App\Administration\Domain\Model\User\UserId;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;

/**
 * Représente une exception ponctuelle sur un créneau récurrent.
 *
 * Une exception peut être une annulation (cancelled) ou une modification
 * (modified) pour une date précise. Cela permet de gérer les changements
 * ponctuels sans altérer la semaine type.
 *
 * @see FR27: Définir une semaine type qui se répète automatiquement
 */
final class ScheduleException
{
    private function __construct(
        public private(set) ScheduleExceptionId $id,
        public private(set) TenantId $tenantId,
        public private(set) ScheduleSlotId $slotId,
        public private(set) DateTimeImmutable $exceptionDate,
        public private(set) ScheduleExceptionType $type,
        public private(set) ?TimeSlot $newTimeSlot,
        public private(set) ?string $newRoom,
        public private(set) ?UserId $newTeacherId,
        public private(set) ?string $reason,
        public private(set) UserId $createdBy,
        public private(set) DateTimeImmutable $createdAt,
    ) {
    }

    /**
     * Annule un cours récurrent pour une date donnée.
     */
    public static function annuler(
        TenantId $tenantId,
        ScheduleSlotId $slotId,
        DateTimeImmutable $exceptionDate,
        ?string $reason,
        UserId $createdBy,
        DateTimeImmutable $now,
    ): self {
        return new self(
            id: ScheduleExceptionId::generate(),
            tenantId: $tenantId,
            slotId: $slotId,
            exceptionDate: $exceptionDate,
            type: ScheduleExceptionType::CANCELLED,
            newTimeSlot: null,
            newRoom: null,
            newTeacherId: null,
            reason: $reason,
            createdBy: $createdBy,
            createdAt: $now,
        );
    }

    /**
     * Modifie un cours récurrent pour une date donnée.
     *
     * Les champs null signifient "pas de changement par rapport au créneau original".
     */
    public static function modifier(
        TenantId $tenantId,
        ScheduleSlotId $slotId,
        DateTimeImmutable $exceptionDate,
        ?TimeSlot $newTimeSlot,
        ?string $newRoom,
        ?UserId $newTeacherId,
        ?string $reason,
        UserId $createdBy,
        DateTimeImmutable $now,
    ): self {
        return new self(
            id: ScheduleExceptionId::generate(),
            tenantId: $tenantId,
            slotId: $slotId,
            exceptionDate: $exceptionDate,
            type: ScheduleExceptionType::MODIFIED,
            newTimeSlot: $newTimeSlot,
            newRoom: $newRoom,
            newTeacherId: $newTeacherId,
            reason: $reason,
            createdBy: $createdBy,
            createdAt: $now,
        );
    }

    public function isCancelled(): bool
    {
        return $this->type === ScheduleExceptionType::CANCELLED;
    }

    public function isModified(): bool
    {
        return $this->type === ScheduleExceptionType::MODIFIED;
    }

    /**
     * @internal Pour usage Infrastructure uniquement
     */
    public static function reconstitute(
        ScheduleExceptionId $id,
        TenantId $tenantId,
        ScheduleSlotId $slotId,
        DateTimeImmutable $exceptionDate,
        ScheduleExceptionType $type,
        ?TimeSlot $newTimeSlot,
        ?string $newRoom,
        ?UserId $newTeacherId,
        ?string $reason,
        UserId $createdBy,
        DateTimeImmutable $createdAt,
    ): self {
        return new self(
            id: $id,
            tenantId: $tenantId,
            slotId: $slotId,
            exceptionDate: $exceptionDate,
            type: $type,
            newTimeSlot: $newTimeSlot,
            newRoom: $newRoom,
            newTeacherId: $newTeacherId,
            reason: $reason,
            createdBy: $createdBy,
            createdAt: $createdAt,
        );
    }
}
