<?php

declare(strict_types=1);

namespace App\Administration\Domain\Model\HomeworkRules;

use App\Administration\Domain\Event\ReglesDevoirsModifiees;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;
use function count;

use DateTimeImmutable;

/**
 * Aggregate Root représentant la configuration des règles de devoirs d'un établissement.
 *
 * Chaque établissement dispose d'une unique configuration de règles (1:1 avec Tenant).
 * Les règles protègent les élèves et familles des devoirs de dernière minute.
 *
 * @see FR81: Configurer règles de timing des devoirs
 */
final class HomeworkRules extends AggregateRoot
{
    public private(set) DateTimeImmutable $updatedAt;

    /**
     * @param HomeworkRule[] $rules
     */
    private function __construct(
        public private(set) HomeworkRulesId $id,
        public private(set) TenantId $tenantId,
        public private(set) array $rules,
        public private(set) EnforcementMode $enforcementMode,
        public private(set) bool $enabled,
        public private(set) DateTimeImmutable $createdAt,
    ) {
        $this->updatedAt = $createdAt;
    }

    /**
     * Crée une configuration de règles par défaut pour un établissement.
     *
     * Par défaut : aucune règle active, mode soft, activé.
     */
    public static function creer(
        TenantId $tenantId,
        DateTimeImmutable $now,
    ): self {
        return new self(
            id: HomeworkRulesId::generate(),
            tenantId: $tenantId,
            rules: [],
            enforcementMode: EnforcementMode::SOFT,
            enabled: true,
            createdAt: $now,
        );
    }

    /**
     * Met à jour les règles, le mode d'application et l'état d'activation.
     *
     * @param HomeworkRule[] $rules
     */
    public function mettreAJour(
        array $rules,
        EnforcementMode $enforcementMode,
        bool $enabled,
        DateTimeImmutable $now,
    ): void {
        $rulesChanged = !$this->rulesEqual($rules);
        $modeChanged = $this->enforcementMode !== $enforcementMode;
        $enabledChanged = $this->enabled !== $enabled;

        if (!$rulesChanged && !$modeChanged && !$enabledChanged) {
            return;
        }

        $previousRules = $this->rules;
        $this->rules = $rules;
        $this->enforcementMode = $enforcementMode;
        $this->enabled = $enabled;
        $this->updatedAt = $now;

        $this->recordEvent(new ReglesDevoirsModifiees(
            homeworkRulesId: $this->id,
            tenantId: $this->tenantId,
            previousRules: array_map(
                static fn (HomeworkRule $rule): array => $rule->toArray(),
                $previousRules,
            ),
            newRules: array_map(
                static fn (HomeworkRule $rule): array => $rule->toArray(),
                $rules,
            ),
            enforcementMode: $enforcementMode,
            enabled: $enabled,
            occurredOn: $now,
        ));
    }

    /**
     * Désactive complètement les règles.
     *
     * Les enseignants peuvent créer des devoirs librement.
     */
    public function desactiver(DateTimeImmutable $now): void
    {
        $this->mettreAJour(
            rules: $this->rules,
            enforcementMode: EnforcementMode::DISABLED,
            enabled: false,
            now: $now,
        );
    }

    /**
     * Réactive les règles avec le mode précédemment configuré.
     */
    public function activer(EnforcementMode $mode, DateTimeImmutable $now): void
    {
        $this->mettreAJour(
            rules: $this->rules,
            enforcementMode: $mode,
            enabled: true,
            now: $now,
        );
    }

    /**
     * Vérifie si les règles sont actives et applicables.
     */
    public function estActif(): bool
    {
        return $this->enabled && $this->enforcementMode->estActif();
    }

    /**
     * @internal Pour usage Infrastructure uniquement
     *
     * @param HomeworkRule[] $rules
     */
    public static function reconstitute(
        HomeworkRulesId $id,
        TenantId $tenantId,
        array $rules,
        EnforcementMode $enforcementMode,
        bool $enabled,
        DateTimeImmutable $createdAt,
        DateTimeImmutable $updatedAt,
    ): self {
        $homeworkRules = new self(
            id: $id,
            tenantId: $tenantId,
            rules: $rules,
            enforcementMode: $enforcementMode,
            enabled: $enabled,
            createdAt: $createdAt,
        );

        $homeworkRules->updatedAt = $updatedAt;

        return $homeworkRules;
    }

    /**
     * @param HomeworkRule[] $other
     */
    private function rulesEqual(array $other): bool
    {
        if (count($this->rules) !== count($other)) {
            return false;
        }

        foreach ($this->rules as $i => $rule) {
            if (!isset($other[$i]) || !$rule->equals($other[$i])) {
                return false;
            }
        }

        return true;
    }
}
