<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Persistence\Doctrine;

use App\Administration\Domain\Exception\SchoolBrandingNotFoundException;
use App\Administration\Domain\Model\SchoolBranding\BrandColor;
use App\Administration\Domain\Model\SchoolBranding\LogoUrl;
use App\Administration\Domain\Model\SchoolBranding\SchoolBranding;
use App\Administration\Domain\Model\SchoolClass\SchoolId;
use App\Administration\Domain\Repository\SchoolBrandingRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use Override;

final readonly class DoctrineSchoolBrandingRepository implements SchoolBrandingRepository
{
    public function __construct(
        private Connection $connection,
    ) {
    }

    #[Override]
    public function save(SchoolBranding $branding): void
    {
        $this->connection->executeStatement(
            'INSERT INTO school_branding (school_id, tenant_id, logo_url, logo_updated_at, primary_color, secondary_color, accent_color, created_at, updated_at)
             VALUES (:school_id, :tenant_id, :logo_url, :logo_updated_at, :primary_color, :secondary_color, :accent_color, :created_at, :updated_at)
             ON CONFLICT (school_id) DO UPDATE SET
                 logo_url = EXCLUDED.logo_url,
                 logo_updated_at = EXCLUDED.logo_updated_at,
                 primary_color = EXCLUDED.primary_color,
                 secondary_color = EXCLUDED.secondary_color,
                 accent_color = EXCLUDED.accent_color,
                 updated_at = EXCLUDED.updated_at',
            [
                'school_id' => (string) $branding->schoolId,
                'tenant_id' => (string) $branding->tenantId,
                'logo_url' => $branding->logoUrl !== null ? (string) $branding->logoUrl : null,
                'logo_updated_at' => $branding->logoUpdatedAt?->format(DateTimeImmutable::ATOM),
                'primary_color' => $branding->primaryColor !== null ? (string) $branding->primaryColor : null,
                'secondary_color' => $branding->secondaryColor !== null ? (string) $branding->secondaryColor : null,
                'accent_color' => $branding->accentColor !== null ? (string) $branding->accentColor : null,
                'created_at' => $branding->createdAt->format(DateTimeImmutable::ATOM),
                'updated_at' => $branding->updatedAt->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function get(SchoolId $schoolId, TenantId $tenantId): SchoolBranding
    {
        $branding = $this->findBySchoolId($schoolId, $tenantId);

        if ($branding === null) {
            throw SchoolBrandingNotFoundException::pourEcole($schoolId);
        }

        return $branding;
    }

    #[Override]
    public function findBySchoolId(SchoolId $schoolId, TenantId $tenantId): ?SchoolBranding
    {
        $row = $this->connection->fetchAssociative(
            'SELECT * FROM school_branding WHERE school_id = :school_id AND tenant_id = :tenant_id',
            [
                'school_id' => (string) $schoolId,
                'tenant_id' => (string) $tenantId,
            ],
        );

        if ($row === false) {
            return null;
        }

        return $this->hydrate($row);
    }

    /**
     * @param array<string, mixed> $row
     */
    private function hydrate(array $row): SchoolBranding
    {
        /** @var string $schoolId */
        $schoolId = $row['school_id'];
        /** @var string $tenantId */
        $tenantId = $row['tenant_id'];
        /** @var string|null $logoUrl */
        $logoUrl = $row['logo_url'];
        /** @var string|null $logoUpdatedAt */
        $logoUpdatedAt = $row['logo_updated_at'];
        /** @var string|null $primaryColor */
        $primaryColor = $row['primary_color'];
        /** @var string|null $secondaryColor */
        $secondaryColor = $row['secondary_color'];
        /** @var string|null $accentColor */
        $accentColor = $row['accent_color'];
        /** @var string $createdAt */
        $createdAt = $row['created_at'];
        /** @var string $updatedAt */
        $updatedAt = $row['updated_at'];

        return SchoolBranding::reconstitute(
            schoolId: SchoolId::fromString($schoolId),
            tenantId: TenantId::fromString($tenantId),
            logoUrl: $logoUrl !== null ? new LogoUrl($logoUrl) : null,
            logoUpdatedAt: $logoUpdatedAt !== null ? new DateTimeImmutable($logoUpdatedAt) : null,
            primaryColor: $primaryColor !== null ? new BrandColor($primaryColor) : null,
            secondaryColor: $secondaryColor !== null ? new BrandColor($secondaryColor) : null,
            accentColor: $accentColor !== null ? new BrandColor($accentColor) : null,
            createdAt: new DateTimeImmutable($createdAt),
            updatedAt: new DateTimeImmutable($updatedAt),
        );
    }
}
