<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\Doctrine;

use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Model\Homework\HomeworkRuleException;
use App\Scolarite\Domain\Model\Homework\HomeworkRuleExceptionId;
use App\Scolarite\Domain\Repository\HomeworkRuleExceptionRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;

use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use Override;

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

    #[Override]
    public function save(HomeworkRuleException $exception): void
    {
        $this->connection->executeStatement(
            'INSERT INTO homework_rule_exceptions (id, tenant_id, homework_id, rule_type, justification, created_by, created_at)
             VALUES (:id, :tenant_id, :homework_id, :rule_type, :justification, :created_by, :created_at)
             ON CONFLICT (id) DO NOTHING',
            [
                'id' => (string) $exception->id,
                'tenant_id' => (string) $exception->tenantId,
                'homework_id' => (string) $exception->homeworkId,
                'rule_type' => $exception->ruleType,
                'justification' => $exception->justification,
                'created_by' => (string) $exception->createdBy,
                'created_at' => $exception->createdAt->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function findByHomework(HomeworkId $homeworkId, TenantId $tenantId): array
    {
        $rows = $this->connection->fetchAllAssociative(
            'SELECT * FROM homework_rule_exceptions
             WHERE homework_id = :homework_id AND tenant_id = :tenant_id
             ORDER BY created_at DESC',
            [
                'homework_id' => (string) $homeworkId,
                'tenant_id' => (string) $tenantId,
            ],
        );

        return array_map($this->hydrate(...), $rows);
    }

    #[Override]
    public function findByTenant(TenantId $tenantId): array
    {
        $rows = $this->connection->fetchAllAssociative(
            'SELECT * FROM homework_rule_exceptions
             WHERE tenant_id = :tenant_id
             ORDER BY created_at DESC',
            ['tenant_id' => (string) $tenantId],
        );

        return array_map($this->hydrate(...), $rows);
    }

    #[Override]
    public function homeworkIdsWithExceptions(TenantId $tenantId): array
    {
        $rows = $this->connection->fetchAllAssociative(
            'SELECT DISTINCT homework_id FROM homework_rule_exceptions WHERE tenant_id = :tenant_id',
            ['tenant_id' => (string) $tenantId],
        );

        return array_map(
            /** @param array<string, mixed> $row */
            static function (array $row): string {
                /** @var string $homeworkId */
                $homeworkId = $row['homework_id'];

                return $homeworkId;
            },
            $rows,
        );
    }

    /** @param array<string, mixed> $row */
    private function hydrate(array $row): HomeworkRuleException
    {
        /** @var string $id */
        $id = $row['id'];
        /** @var string $tenantId */
        $tenantId = $row['tenant_id'];
        /** @var string $homeworkId */
        $homeworkId = $row['homework_id'];
        /** @var string $ruleType */
        $ruleType = $row['rule_type'];
        /** @var string $justification */
        $justification = $row['justification'];
        /** @var string $createdBy */
        $createdBy = $row['created_by'];
        /** @var string $createdAt */
        $createdAt = $row['created_at'];

        return HomeworkRuleException::reconstitute(
            id: HomeworkRuleExceptionId::fromString($id),
            tenantId: TenantId::fromString($tenantId),
            homeworkId: HomeworkId::fromString($homeworkId),
            ruleType: $ruleType,
            justification: $justification,
            createdBy: UserId::fromString($createdBy),
            createdAt: new DateTimeImmutable($createdAt),
        );
    }
}
