<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Persistence\Doctrine;

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

use function array_map;

use DateTimeImmutable;
use Doctrine\DBAL\Connection;

use function json_encode;

use const JSON_THROW_ON_ERROR;

use Override;

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

    #[Override]
    public function record(
        TenantId $tenantId,
        ?array $previousRules,
        array $newRules,
        EnforcementMode $enforcementMode,
        bool $enabled,
        UserId $changedBy,
        DateTimeImmutable $changedAt,
    ): void {
        $previousJson = $previousRules !== null
            ? json_encode(
                array_map(static fn (HomeworkRule $r): array => $r->toArray(), $previousRules),
                JSON_THROW_ON_ERROR,
            )
            : null;

        $newJson = json_encode(
            array_map(static fn (HomeworkRule $r): array => $r->toArray(), $newRules),
            JSON_THROW_ON_ERROR,
        );

        $this->connection->executeStatement(
            'INSERT INTO homework_rules_history (tenant_id, previous_rules, new_rules, enforcement_mode, enabled, changed_by, changed_at)
             VALUES (:tenant_id, :previous_rules, :new_rules, :enforcement_mode, :enabled, :changed_by, :changed_at)',
            [
                'tenant_id' => (string) $tenantId,
                'previous_rules' => $previousJson,
                'new_rules' => $newJson,
                'enforcement_mode' => $enforcementMode->value,
                'enabled' => $enabled ? 'true' : 'false',
                'changed_by' => (string) $changedBy,
                'changed_at' => $changedAt->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function findByTenant(TenantId $tenantId, int $limit = 50): array
    {
        /** @var array<array{id: string, previous_rules: string|null, new_rules: string, enforcement_mode: string, enabled: bool, changed_by: string, changed_at: string}> $rows */
        $rows = $this->connection->fetchAllAssociative(
            'SELECT id, previous_rules, new_rules, enforcement_mode, enabled, changed_by, changed_at
             FROM homework_rules_history
             WHERE tenant_id = :tenant_id
             ORDER BY changed_at DESC
             LIMIT :limit',
            ['tenant_id' => (string) $tenantId, 'limit' => $limit],
        );

        return $rows;
    }
}
