<?php

declare(strict_types=1);

namespace App\Catalog;

use App\Config\CatalogIntentConfig;
use App\Entity\Document;
use App\Tag\TagTypes;
use App\Tag\TagVectorSearchClient;
use Doctrine\DBAL\Connection;
use Symfony\Component\Uid\Uuid;

/**
 * Builds deterministic catalog lists from a validated catalog entity term.
 *
 * This service is intentionally conservative:
 * - only strong catalog_entity matches may open the catalog path
 * - ambiguous matches fall back to normal retrieval
 * - only ACTIVE documents are listed
 */
final class EntityCatalogService
{
    public function __construct(
        private readonly TagVectorSearchClient $tagVectorClient,
        private readonly Connection $connection,
        private readonly CatalogIntentConfig $config,
    ) {
    }

    /**
     * Returns a catalog text block or null when no safe catalog path exists.
     */
    public function listByTerm(string $entityTerm): ?string
    {
        $entityTerm = trim($entityTerm);

        if ($entityTerm === '') {
            return null;
        }

        $hits = $this->tagVectorClient->search($entityTerm, $this->config->getListSearchLimit());

        if ($hits === []) {
            return null;
        }

        $best = $hits[0];
        $bestScore = (float) ($best['score'] ?? 0.0);

        if (!$this->config->isScoreAccepted($bestScore)) {
            return null;
        }

        if (($best['tag_type'] ?? null) !== TagTypes::CATALOG_ENTITY) {
            return null;
        }

        if (isset($hits[1])) {
            $secondScore = (float) ($hits[1]['score'] ?? 0.0);

            if ($this->config->isAmbiguous($bestScore, $secondScore)) {
                return null;
            }
        }

        $tagId = trim((string) ($best['tag_id'] ?? ''));

        if ($tagId === '') {
            return null;
        }

        try {
            $tagBinaryId = Uuid::fromString($tagId)->toBinary();
        } catch (\Throwable) {
            return null;
        }

        $tagLabel = trim((string) ($best['label'] ?? ''));

        $rows = $this->connection->fetchAllAssociative(
            '
            SELECT DISTINCT d.title
            FROM document d
            INNER JOIN document_tag dt ON dt.document_id = d.id
            WHERE dt.tag_id = :tagId
              AND d.status = :status
            ORDER BY d.title ASC
            ',
            [
                'tagId' => $tagBinaryId,
                'status' => Document::STATUS_ACTIVE,
            ]
        );

        if ($rows === []) {
            return null;
        }

        $titles = [];

        foreach ($rows as $row) {
            $title = trim((string) ($row['title'] ?? ''));

            if ($title === '') {
                continue;
            }

            $titles[$title] = $title;
        }

        if ($titles === []) {
            return null;
        }

        return $this->buildTextBlock(
            $tagLabel !== '' ? $tagLabel : null,
            array_values($titles)
        );
    }

    /**
     * Builds a stable human-readable list block for the prompt.
     *
     * @param list<string> $titles
     */
    private function buildTextBlock(?string $tagLabel, array $titles): string
    {
        $headline = 'Folgende Einträge sind verfügbar:';

        if ($tagLabel !== null && trim($tagLabel) !== '') {
            $headline = sprintf('Folgende %s sind verfügbar:', trim($tagLabel));
        }

        $lines = [];

        foreach ($titles as $title) {
            $lines[] = '- ' . $title;
        }

        return $headline . "\n\n" . implode("\n", $lines);
    }
}