<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\Doctrine;

use App\Administration\Domain\Model\User\UserId;
use App\Scolarite\Domain\Model\Grade\AppreciationCategory;
use App\Scolarite\Domain\Model\Grade\AppreciationTemplate;
use App\Scolarite\Domain\Model\Grade\AppreciationTemplateId;
use App\Scolarite\Domain\Repository\AppreciationTemplateRepository;
use App\Shared\Domain\Tenant\TenantId;

use function array_map;

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

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

    #[Override]
    public function save(AppreciationTemplate $template): void
    {
        $this->connection->executeStatement(
            'INSERT INTO appreciation_templates (id, tenant_id, teacher_id, title, content, category, usage_count, created_at, updated_at)
             VALUES (:id, :tenant_id, :teacher_id, :title, :content, :category, :usage_count, :created_at, :updated_at)
             ON CONFLICT (id) DO UPDATE SET
                 title = EXCLUDED.title,
                 content = EXCLUDED.content,
                 category = EXCLUDED.category,
                 usage_count = EXCLUDED.usage_count,
                 updated_at = EXCLUDED.updated_at',
            [
                'id' => (string) $template->id,
                'tenant_id' => (string) $template->tenantId,
                'teacher_id' => (string) $template->teacherId,
                'title' => $template->title,
                'content' => $template->content,
                'category' => $template->category?->value,
                'usage_count' => $template->usageCount,
                'created_at' => $template->createdAt->format(DateTimeImmutable::ATOM),
                'updated_at' => $template->updatedAt->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function findById(AppreciationTemplateId $id, TenantId $tenantId): ?AppreciationTemplate
    {
        $row = $this->connection->fetchAssociative(
            'SELECT * FROM appreciation_templates WHERE id = :id AND tenant_id = :tenant_id',
            ['id' => (string) $id, 'tenant_id' => (string) $tenantId],
        );

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

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

    #[Override]
    public function findByTeacher(UserId $teacherId, TenantId $tenantId): array
    {
        $rows = $this->connection->fetchAllAssociative(
            'SELECT * FROM appreciation_templates
             WHERE teacher_id = :teacher_id AND tenant_id = :tenant_id
             ORDER BY usage_count DESC, title ASC',
            [
                'teacher_id' => (string) $teacherId,
                'tenant_id' => (string) $tenantId,
            ],
        );

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

    #[Override]
    public function delete(AppreciationTemplateId $id, TenantId $tenantId): void
    {
        $this->connection->executeStatement(
            'DELETE FROM appreciation_templates WHERE id = :id AND tenant_id = :tenant_id',
            ['id' => (string) $id, 'tenant_id' => (string) $tenantId],
        );
    }

    /** @param array<string, mixed> $row */
    private function hydrate(array $row): AppreciationTemplate
    {
        /** @var string $id */
        $id = $row['id'];
        /** @var string $tenantId */
        $tenantId = $row['tenant_id'];
        /** @var string $teacherId */
        $teacherId = $row['teacher_id'];
        /** @var string $title */
        $title = $row['title'];
        /** @var string $content */
        $content = $row['content'];
        /** @var string|null $category */
        $category = $row['category'] ?? null;
        /** @var int|string $usageCount */
        $usageCount = $row['usage_count'];
        /** @var string $createdAt */
        $createdAt = $row['created_at'];
        /** @var string $updatedAt */
        $updatedAt = $row['updated_at'];

        return AppreciationTemplate::reconstitute(
            id: AppreciationTemplateId::fromString($id),
            tenantId: TenantId::fromString($tenantId),
            teacherId: UserId::fromString($teacherId),
            title: $title,
            content: $content,
            category: $category !== null ? AppreciationCategory::from($category) : null,
            usageCount: (int) $usageCount,
            createdAt: new DateTimeImmutable($createdAt),
            updatedAt: new DateTimeImmutable($updatedAt),
        );
    }
}
