<?php

declare(strict_types=1);

namespace App\Vector;

use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

final class VectorSearchClient
{
    private HttpClientInterface $http;
    private string $serviceUrl;
    private LoggerInterface $agentLogger;

    public function __construct(
        HttpClientInterface $http,
        string $serviceUrl,
        LoggerInterface $agentLogger,
        private readonly float $minScore = 0.30,
        private readonly int $maxLimit = 200,
        private readonly int $timeoutSeconds = 10,
    ) {
        $this->http = $http;
        $this->serviceUrl = rtrim($serviceUrl, '/');
        $this->agentLogger = $agentLogger;
    }

    /**
     * Standard global search.
     *
     * @return array<int, array{
     *     chunk_id:string,
     *     score:float,
     *     document_id:?string,
     *     chunk_index:?int
     * }>
     */
    public function search(string $query, int $limit = 5): array
    {
        return $this->executeSearch([
            'query' => trim($query),
            'limit' => $this->clampLimit($limit),
        ]);
    }

    /**
     * Scoped search: only inside specific documents.
     *
     * @param array<int,string> $docIds
     *
     * @return array<int, array{
     *     chunk_id:string,
     *     score:float,
     *     document_id:?string,
     *     chunk_index:?int
     * }>
     */
    public function searchScoped(
        string $query,
        int $limit,
        array $docIds
    ): array {
        if ($docIds === []) {
            return [];
        }

        return $this->executeSearch([
            'query'   => trim($query),
            'limit'   => $this->clampLimit($limit),
            'doc_ids' => array_values($docIds),
        ]);
    }

    /**
     * Shared HTTP logic.
     *
     * @param array<string,mixed> $payload
     *
     * @return array<int, array{
     *     chunk_id:string,
     *     score:float,
     *     document_id:?string,
     *     chunk_index:?int
     * }>
     */
    private function executeSearch(array $payload): array
    {
        try {
            $response = $this->http->request(
                'POST',
                $this->serviceUrl . '/search-chunks',
                [
                    'json' => $payload,
                    'timeout' => $this->timeoutSeconds,
                ]
            );

            if ($response->getStatusCode() !== 200) {
                $this->agentLogger->error(
                    'Vector service returned non-200 (chunks)',
                    ['status' => $response->getStatusCode()]
                );
                return [];
            }

            $data = $response->toArray(false);

        } catch (\Throwable $e) {
            $this->agentLogger->error(
                'Vector service unreachable (chunks)',
                ['error' => $e->getMessage()]
            );
            return [];
        }

        if (!is_array($data)) {
            $this->agentLogger->warning('Vector service returned invalid payload (chunks)');
            return [];
        }

        $filtered = [];

        foreach ($data as $row) {
            if (!is_array($row)) {
                continue;
            }

            $chunkId = (string)($row['chunk_id'] ?? '');
            $score   = $row['score'] ?? null;

            if ($chunkId === '' || !is_numeric($score)) {
                continue;
            }

            $score = (float)$score;

            if ($score < $this->minScore) {
                continue;
            }

            $documentId = null;
            if (isset($row['document_id']) && is_string($row['document_id']) && $row['document_id'] !== '') {
                $documentId = $row['document_id'];
            }

            $chunkIndex = null;
            if (isset($row['chunk_index'])) {
                if (is_int($row['chunk_index'])) {
                    $chunkIndex = $row['chunk_index'];
                } elseif (is_string($row['chunk_index']) && ctype_digit($row['chunk_index'])) {
                    $chunkIndex = (int)$row['chunk_index'];
                }
            }

            $filtered[] = [
                'chunk_id'    => $chunkId,
                'score'       => $score,
                'document_id' => $documentId,
                'chunk_index' => $chunkIndex,
            ];
        }

        return $filtered;
    }

    private function clampLimit(int $limit): int
    {
        if ($limit < 1) {
            return 1;
        }

        if ($limit > $this->maxLimit) {
            return $this->maxLimit;
        }

        return $limit;
    }
}
