<?php

declare(strict_types=1);

namespace App\Administration\Domain\Model\SchoolBranding;

use App\Administration\Domain\Event\BrandingModifie;
use App\Administration\Domain\Model\SchoolClass\SchoolId;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;

/**
 * Aggregate Root représentant la personnalisation visuelle d'un établissement.
 *
 * Chaque établissement a un branding unique (1:1 avec School).
 * Le branding comprend un logo, une couleur principale, une couleur secondaire
 * et une couleur d'accent. Toutes les personnalisations sont optionnelles :
 * en l'absence de valeurs, le thème par défaut Classeo est utilisé.
 *
 * @see FR83: Configurer logo et couleurs établissement
 */
final class SchoolBranding extends AggregateRoot
{
    public private(set) ?LogoUrl $logoUrl = null;
    public private(set) ?DateTimeImmutable $logoUpdatedAt = null;
    public private(set) ?BrandColor $primaryColor = null;
    public private(set) ?BrandColor $secondaryColor = null;
    public private(set) ?BrandColor $accentColor = null;
    public private(set) DateTimeImmutable $updatedAt;

    private function __construct(
        public private(set) SchoolId $schoolId,
        public private(set) TenantId $tenantId,
        public private(set) DateTimeImmutable $createdAt,
    ) {
        $this->updatedAt = $createdAt;
    }

    /**
     * Crée un branding vide pour un établissement (thème par défaut).
     */
    public static function creer(
        SchoolId $schoolId,
        TenantId $tenantId,
        DateTimeImmutable $createdAt,
    ): self {
        return new self(
            schoolId: $schoolId,
            tenantId: $tenantId,
            createdAt: $createdAt,
        );
    }

    /**
     * Modifie les couleurs du branding.
     */
    public function modifierCouleurs(
        ?BrandColor $primaryColor,
        ?BrandColor $secondaryColor,
        ?BrandColor $accentColor,
        DateTimeImmutable $at,
    ): void {
        $primarySame = $this->colorsEqual($this->primaryColor, $primaryColor);
        $secondarySame = $this->colorsEqual($this->secondaryColor, $secondaryColor);
        $accentSame = $this->colorsEqual($this->accentColor, $accentColor);

        if ($primarySame && $secondarySame && $accentSame) {
            return;
        }

        $this->primaryColor = $primaryColor;
        $this->secondaryColor = $secondaryColor;
        $this->accentColor = $accentColor;
        $this->updatedAt = $at;

        $this->recordEvent(new BrandingModifie(
            schoolId: $this->schoolId,
            tenantId: $this->tenantId,
            champ: 'couleurs',
            occurredOn: $at,
        ));
    }

    /**
     * Change le logo de l'établissement.
     */
    public function changerLogo(LogoUrl $logoUrl, DateTimeImmutable $at): void
    {
        if ($this->logoUrl !== null && $this->logoUrl->equals($logoUrl)) {
            return;
        }

        $this->logoUrl = $logoUrl;
        $this->logoUpdatedAt = $at;
        $this->updatedAt = $at;

        $this->recordEvent(new BrandingModifie(
            schoolId: $this->schoolId,
            tenantId: $this->tenantId,
            champ: 'logo',
            occurredOn: $at,
        ));
    }

    /**
     * Supprime le logo de l'établissement.
     */
    public function supprimerLogo(DateTimeImmutable $at): void
    {
        if ($this->logoUrl === null) {
            return;
        }

        $this->logoUrl = null;
        $this->logoUpdatedAt = null;
        $this->updatedAt = $at;

        $this->recordEvent(new BrandingModifie(
            schoolId: $this->schoolId,
            tenantId: $this->tenantId,
            champ: 'logo',
            occurredOn: $at,
        ));
    }

    /**
     * Réinitialise le branding vers le thème par défaut.
     */
    public function reinitialiser(DateTimeImmutable $at): void
    {
        if (!$this->estPersonnalise()) {
            return;
        }

        $this->logoUrl = null;
        $this->logoUpdatedAt = null;
        $this->primaryColor = null;
        $this->secondaryColor = null;
        $this->accentColor = null;
        $this->updatedAt = $at;

        $this->recordEvent(new BrandingModifie(
            schoolId: $this->schoolId,
            tenantId: $this->tenantId,
            champ: 'reinitialisation',
            occurredOn: $at,
        ));
    }

    /**
     * Vérifie si l'établissement a personnalisé son branding.
     */
    public function estPersonnalise(): bool
    {
        return $this->logoUrl !== null
            || $this->primaryColor !== null
            || $this->secondaryColor !== null
            || $this->accentColor !== null;
    }

    /**
     * Reconstitue un SchoolBranding depuis le stockage.
     *
     * @internal Pour usage Infrastructure uniquement
     */
    public static function reconstitute(
        SchoolId $schoolId,
        TenantId $tenantId,
        ?LogoUrl $logoUrl,
        ?DateTimeImmutable $logoUpdatedAt,
        ?BrandColor $primaryColor,
        ?BrandColor $secondaryColor,
        ?BrandColor $accentColor,
        DateTimeImmutable $createdAt,
        DateTimeImmutable $updatedAt,
    ): self {
        $branding = new self(
            schoolId: $schoolId,
            tenantId: $tenantId,
            createdAt: $createdAt,
        );

        $branding->logoUrl = $logoUrl;
        $branding->logoUpdatedAt = $logoUpdatedAt;
        $branding->primaryColor = $primaryColor;
        $branding->secondaryColor = $secondaryColor;
        $branding->accentColor = $accentColor;
        $branding->updatedAt = $updatedAt;

        return $branding;
    }

    private function colorsEqual(?BrandColor $a, ?BrandColor $b): bool
    {
        if ($a === null && $b === null) {
            return true;
        }

        if ($a !== null && $b !== null) {
            return $a->equals($b);
        }

        return false;
    }
}
