<?php

declare(strict_types=1);

namespace App\Scolarite\Infrastructure\Persistence\Doctrine;

use App\Scolarite\Domain\Model\Homework\HomeworkAttachment;
use App\Scolarite\Domain\Model\Homework\HomeworkAttachmentId;
use App\Scolarite\Domain\Model\Homework\HomeworkId;
use App\Scolarite\Domain\Repository\HomeworkAttachmentRepository;

use function array_fill_keys;
use function array_map;

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

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

    #[Override]
    public function findByHomeworkId(HomeworkId $homeworkId): array
    {
        $rows = $this->connection->fetchAllAssociative(
            'SELECT * FROM homework_attachments WHERE homework_id = :homework_id',
            ['homework_id' => (string) $homeworkId],
        );

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

    #[Override]
    public function hasAttachments(HomeworkId ...$homeworkIds): array
    {
        if ($homeworkIds === []) {
            return [];
        }

        $ids = array_map(static fn (HomeworkId $id): string => (string) $id, $homeworkIds);

        /** @var array<array{homework_id: string}> $rows */
        $rows = $this->connection->fetchAllAssociative(
            'SELECT DISTINCT homework_id FROM homework_attachments WHERE homework_id IN (:ids)',
            ['ids' => $ids],
            ['ids' => ArrayParameterType::STRING],
        );

        $result = array_fill_keys($ids, false);

        foreach ($rows as $row) {
            /** @var string $hwId */
            $hwId = $row['homework_id'];
            $result[$hwId] = true;
        }

        return $result;
    }

    #[Override]
    public function save(HomeworkId $homeworkId, HomeworkAttachment $attachment): void
    {
        $this->connection->executeStatement(
            'INSERT INTO homework_attachments (id, homework_id, filename, file_path, file_size, mime_type, uploaded_at)
             VALUES (:id, :homework_id, :filename, :file_path, :file_size, :mime_type, :uploaded_at)',
            [
                'id' => (string) $attachment->id,
                'homework_id' => (string) $homeworkId,
                'filename' => $attachment->filename,
                'file_path' => $attachment->filePath,
                'file_size' => $attachment->fileSize,
                'mime_type' => $attachment->mimeType,
                'uploaded_at' => $attachment->uploadedAt->format(DateTimeImmutable::ATOM),
            ],
        );
    }

    #[Override]
    public function delete(HomeworkId $homeworkId, HomeworkAttachment $attachment): void
    {
        $this->connection->executeStatement(
            'DELETE FROM homework_attachments WHERE id = :id AND homework_id = :homework_id',
            [
                'id' => (string) $attachment->id,
                'homework_id' => (string) $homeworkId,
            ],
        );
    }

    /** @param array<string, mixed> $row */
    private function hydrate(array $row): HomeworkAttachment
    {
        /** @var string $id */
        $id = $row['id'];
        /** @var string $filename */
        $filename = $row['filename'];
        /** @var string $filePath */
        $filePath = $row['file_path'];
        /** @var string|int $rawFileSize */
        $rawFileSize = $row['file_size'];
        $fileSize = (int) $rawFileSize;
        /** @var string $mimeType */
        $mimeType = $row['mime_type'];
        /** @var string $uploadedAt */
        $uploadedAt = $row['uploaded_at'];

        return new HomeworkAttachment(
            id: HomeworkAttachmentId::fromString($id),
            filename: $filename,
            filePath: $filePath,
            fileSize: $fileSize,
            mimeType: $mimeType,
            uploadedAt: new DateTimeImmutable($uploadedAt),
        );
    }
}
