<?php

declare(strict_types=1);

namespace App\Administration\Application\Command\UpdateHomeworkRules;

use App\Administration\Domain\Model\HomeworkRules\EnforcementMode;
use App\Administration\Domain\Model\HomeworkRules\HomeworkRule;
use App\Administration\Domain\Model\HomeworkRules\HomeworkRules;
use App\Administration\Domain\Model\User\UserId;
use App\Administration\Domain\Repository\HomeworkRulesHistoryRepository;
use App\Administration\Domain\Repository\HomeworkRulesRepository;
use App\Shared\Domain\Clock;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;
use function count;

use Symfony\Component\Messenger\Attribute\AsMessageHandler;

/**
 * Orchestre la mise à jour des règles de devoirs.
 *
 * Crée la configuration si elle n'existe pas encore pour le tenant.
 * Enregistre l'historique des changements pour l'audit (AC6).
 *
 * L'historique est conditionné sur un changement effectif (comparaison avant/après).
 * Les domain events ne sont PAS consommés ici — ils restent disponibles pour
 * le Processor qui les dispatche sur l'event bus.
 */
#[AsMessageHandler(bus: 'command.bus')]
final readonly class UpdateHomeworkRulesHandler
{
    public function __construct(
        private HomeworkRulesRepository $homeworkRulesRepository,
        private HomeworkRulesHistoryRepository $historyRepository,
        private Clock $clock,
    ) {
    }

    public function __invoke(UpdateHomeworkRulesCommand $command): HomeworkRules
    {
        $tenantId = TenantId::fromString($command->tenantId);
        $enforcementMode = EnforcementMode::from($command->enforcementMode);
        $now = $this->clock->now();
        $rules = array_map(HomeworkRule::fromArray(...), $command->rules);

        $homeworkRules = $this->homeworkRulesRepository->findByTenantId($tenantId);

        // Capturer l'état précédent pour l'historique.
        // null = première configuration du tenant (aucune règle avant).
        $previousRules = $homeworkRules?->rules;
        $previousMode = $homeworkRules?->enforcementMode;
        $previousEnabled = $homeworkRules?->enabled;

        if ($homeworkRules === null) {
            $homeworkRules = HomeworkRules::creer(tenantId: $tenantId, now: $now);
        }

        $homeworkRules->mettreAJour(
            rules: $rules,
            enforcementMode: $enforcementMode,
            enabled: $command->enabled,
            now: $now,
        );

        $this->homeworkRulesRepository->save($homeworkRules);

        // Enregistrer l'historique uniquement si quelque chose a changé.
        // On compare l'état avant/après au lieu de consommer pullDomainEvents(),
        // car le Processor en a besoin pour dispatcher sur l'event bus.
        $changed = $previousRules === null
            || $previousMode !== $enforcementMode
            || $previousEnabled !== $command->enabled
            || !$this->rulesEqual($previousRules, $rules);

        if ($changed) {
            $this->historyRepository->record(
                tenantId: $tenantId,
                previousRules: $previousRules,
                newRules: $rules,
                enforcementMode: $enforcementMode,
                enabled: $command->enabled,
                changedBy: UserId::fromString($command->changedBy),
                changedAt: $now,
            );
        }

        return $homeworkRules;
    }

    /**
     * @param HomeworkRule[] $a
     * @param HomeworkRule[] $b
     */
    private function rulesEqual(array $a, array $b): bool
    {
        if (count($a) !== count($b)) {
            return false;
        }

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

        return true;
    }
}
