using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Hc900Crawler.Infrastructure.Kb;

/// <summary>
/// Qdrant HTTP API 래퍼. nomic-embed-text 임베딩이 768-dim 이므로 동일하게 가정.
/// </summary>
public sealed class KbQdrantClient
{
    private readonly HttpClient _http;
    private readonly ILogger<KbQdrantClient> _logger;
    private readonly int _vectorSize;

    public KbQdrantClient(IHttpClientFactory httpFactory, IConfiguration config, ILogger<KbQdrantClient> logger)
    {
        _http = httpFactory.CreateClient("KbQdrant");
        _vectorSize = int.TryParse(config["Kb:VectorSize"], out var v) ? v : 768;
        _logger = logger;
    }

    public async Task<bool> EnsureCollectionAsync(string name, CancellationToken ct = default)
    {
        var get = await _http.GetAsync($"/collections/{name}", ct);
        if (get.StatusCode == HttpStatusCode.OK) return true;
        if (get.StatusCode != HttpStatusCode.NotFound)
        {
            _logger.LogWarning("[Kb][Qdrant] 컬렉션 조회 실패 {Name} HTTP {Code}", name, (int)get.StatusCode);
        }

        var body = new
        {
            vectors = new { size = _vectorSize, distance = "Cosine" }
        };
        var res = await _http.PutAsJsonAsync($"/collections/{name}", body, ct);
        if (res.IsSuccessStatusCode)
        {
            _logger.LogInformation("[Kb][Qdrant] 컬렉션 생성 {Name} (size={Size})", name, _vectorSize);
            return true;
        }
        var err = await res.Content.ReadAsStringAsync(ct);
        _logger.LogError("[Kb][Qdrant] 컬렉션 생성 실패 {Name} HTTP {Code}: {Err}", name, (int)res.StatusCode, err);
        return false;
    }

    public async Task<bool> DeleteByDocAsync(string collection, Guid docId, CancellationToken ct = default)
    {
        var body = new
        {
            filter = new
            {
                must = new[]
                {
                    new { key = "doc_id", match = new { value = docId.ToString() } }
                }
            }
        };
        var res = await _http.PostAsJsonAsync($"/collections/{collection}/points/delete?wait=true", body, ct);
        return res.IsSuccessStatusCode;
    }

    public async Task<bool> UpsertAsync(string collection, IEnumerable<QdrantPoint> points, CancellationToken ct = default)
    {
        var body = new { points };
        var res = await _http.PutAsJsonAsync($"/collections/{collection}/points?wait=true", body, ct);
        if (!res.IsSuccessStatusCode)
        {
            var err = await res.Content.ReadAsStringAsync(ct);
            _logger.LogError("[Kb][Qdrant] upsert 실패 {Name} HTTP {Code}: {Err}", collection, (int)res.StatusCode, err);
            return false;
        }
        return true;
    }

    /// <summary>
    /// Scroll API로 doc_id 필터에 해당하는 청크 payload 조회.
    /// vector는 가져오지 않음(payload only).
    /// </summary>
    public async Task<List<Dictionary<string, JsonElement>>> GetChunksByDocIdAsync(
        string collection, Guid docId, int limit = 200, CancellationToken ct = default)
    {
        var body = new
        {
            filter = new
            {
                must = new[]
                {
                    new { key = "doc_id", match = new { value = docId.ToString() } }
                }
            },
            limit,
            with_payload = true,
            with_vector = false
        };
        var res = await _http.PostAsJsonAsync($"/collections/{collection}/points/scroll", body, ct);
        var chunks = new List<Dictionary<string, JsonElement>>();
        if (!res.IsSuccessStatusCode)
        {
            var err = await res.Content.ReadAsStringAsync(ct);
            _logger.LogWarning("[Kb][Qdrant] scroll 실패 {Name} HTTP {Code}: {Err}", collection, (int)res.StatusCode, err);
            return chunks;
        }
        var json = await res.Content.ReadFromJsonAsync<JsonElement>(cancellationToken: ct);
        if (json.ValueKind == JsonValueKind.Object &&
            json.TryGetProperty("result", out var result) &&
            result.TryGetProperty("points", out var points) &&
            points.ValueKind == JsonValueKind.Array)
        {
            foreach (var p in points.EnumerateArray())
            {
                if (p.TryGetProperty("payload", out var payload) && payload.ValueKind == JsonValueKind.Object)
                {
                    var dict = new Dictionary<string, JsonElement>();
                    foreach (var prop in payload.EnumerateObject())
                        dict[prop.Name] = prop.Value.Clone();
                    chunks.Add(dict);
                }
            }
        }
        return chunks;
    }
}

public sealed class QdrantPoint
{
    public Guid id { get; set; }
    public float[] vector { get; set; } = Array.Empty<float>();
    public Dictionary<string, object?> payload { get; set; } = new();
}
