<?php

declare(strict_types=1);

namespace App\Administration\Infrastructure\Persistence\Doctrine;

use App\Administration\Domain\Model\Import\KnownImportFormat;
use App\Administration\Domain\Model\Import\StudentImportField;
use App\Administration\Domain\Repository\SavedColumnMappingRepository;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use Doctrine\DBAL\Connection;

use function json_decode;
use function json_encode;

use const JSON_THROW_ON_ERROR;

use Override;

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

    #[Override]
    public function save(TenantId $tenantId, KnownImportFormat $format, array $mapping): void
    {
        $serialized = [];
        foreach ($mapping as $column => $field) {
            $serialized[$column] = $field->value;
        }

        $this->connection->executeStatement(
            'INSERT INTO saved_column_mappings (tenant_id, format, mapping_data, saved_at)
             VALUES (:tenant_id, :format, :mapping_data, :saved_at)
             ON CONFLICT (tenant_id, format) DO UPDATE SET
                 mapping_data = EXCLUDED.mapping_data,
                 saved_at = EXCLUDED.saved_at',
            [
                'tenant_id' => (string) $tenantId,
                'format' => $format->value,
                'mapping_data' => json_encode($serialized, JSON_THROW_ON_ERROR),
                'saved_at' => (new DateTimeImmutable())->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function findByTenantAndFormat(TenantId $tenantId, KnownImportFormat $format): ?array
    {
        $row = $this->connection->fetchAssociative(
            'SELECT mapping_data FROM saved_column_mappings WHERE tenant_id = :tenant_id AND format = :format',
            [
                'tenant_id' => (string) $tenantId,
                'format' => $format->value,
            ],
        );

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

        /** @var string $json */
        $json = $row['mapping_data'];

        /** @var array<string, string> $data */
        $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);

        $mapping = [];
        foreach ($data as $column => $fieldValue) {
            $field = StudentImportField::tryFrom($fieldValue);
            if ($field !== null) {
                $mapping[$column] = $field;
            }
        }

        return $mapping !== [] ? $mapping : null;
    }
}
