using ChatRAG.Models; using ChatRAG.Services.Contracts; using ChatRAG.Settings.ChatRAG.Configuration; using Microsoft.Extensions.Options; using System.Text; using System.Text.Json; using Qdrant.Client; using Qdrant.Client.Grpc; using Google.Protobuf; namespace ChatRAG.Data { public class QdrantProjectDataRepository : IProjectDataRepository { private readonly HttpClient _httpClient; private readonly string _collectionName; private readonly ILogger _logger; private readonly QdrantClient _qdrantClient; private volatile bool _collectionInitialized = false; private readonly SemaphoreSlim _initializationSemaphore = new(1, 1); public QdrantProjectDataRepository( IOptions settings, HttpClient httpClient, ILogger logger) { var qdrantSettings = settings.Value.Qdrant ?? throw new ArgumentNullException("Qdrant settings not configured"); _httpClient = httpClient; _httpClient.BaseAddress = new Uri($"http://{qdrantSettings.Host}:{qdrantSettings.Port}"); _collectionName = qdrantSettings.GroupsCollectionName; _logger = logger; // Inicializa o QdrantClient - use GRPC (porta 6334) para melhor performance _qdrantClient = new QdrantClient(qdrantSettings.Host, port: 6334, https: false); InitializeAsync().GetAwaiter().GetResult(); } private async Task InitializeAsync() { try { if (_collectionInitialized) return; await _initializationSemaphore.WaitAsync(); var exists = await _qdrantClient.CollectionExistsAsync(_collectionName); if (!exists) { await CreateProjectsCollection(); } _collectionInitialized = true; } catch (Exception ex) { _logger.LogError(ex, "Erro ao inicializar collection de projetos no Qdrant"); } finally { _initializationSemaphore.Release(); } } public async Task> GetAsync() { try { //var scrollRequest = new ScrollPoints //{ // CollectionName = _collectionName, // Filter = new Filter(), // Filtro vazio // Limit = 1000, // WithPayload = true, // WithVectors = false //}; //var result = await _qdrantClient.ScrollAsync(_collectionName, scrollRequest); var result = await _qdrantClient.ScrollAsync(_collectionName, new Filter(), 1000, null, true, false); return result.Result.Select(ConvertToProject) .Where(p => p != null) .ToList()!; } catch (Exception ex) { _logger.LogError(ex, "Erro ao recuperar projetos do Qdrant"); return new List(); } } public async Task GetAsync(string id) { try { var points = await _qdrantClient.RetrieveAsync( _collectionName, new[] { new PointId { Uuid = id } }, withPayload: true, withVectors: false ); var point = points.FirstOrDefault(); return point != null ? ConvertToProject(point) : null; } catch (Exception ex) { _logger.LogError(ex, "Erro ao buscar projeto {Id} no Qdrant", id); return null; } } public async Task CreateAsync(Project newProject) { try { var id = string.IsNullOrEmpty(newProject.Id) ? Guid.NewGuid().ToString() : newProject.Id; newProject.Id = id; var point = new PointStruct { Id = new PointId { Uuid= id }, Vectors = new float[384], // Vector dummy para projetos Payload = { ["id"] = newProject.Id, ["nome"] = newProject.Nome, ["descricao"] = newProject.Descricao, ["created_at"] = DateTime.UtcNow.ToString("O"), ["entity_type"] = "project" } }; await _qdrantClient.UpsertAsync(_collectionName, new[] { point }); } catch (Exception ex) { _logger.LogError(ex, "Erro ao criar projeto no Qdrant"); throw; } } public async Task UpdateAsync(string id, Project updatedProject) { try { updatedProject.Id = id; var point = new PointStruct { Id = new PointId { Uuid = id }, Vectors = new float[384], // Vector dummy Payload = { ["id"] = updatedProject.Id, ["nome"] = updatedProject.Nome, ["descricao"] = updatedProject.Descricao, ["updated_at"] = DateTime.UtcNow.ToString("O"), ["entity_type"] = "project" } }; await _qdrantClient.UpsertAsync(_collectionName, new[] { point }); } catch (Exception ex) { _logger.LogError(ex, "Erro ao atualizar projeto {Id} no Qdrant", id); throw; } } public async Task SaveAsync(Project project) { try { if (string.IsNullOrEmpty(project.Id)) { await CreateAsync(project); } else { var existing = await GetAsync(project.Id); if (existing == null) { await CreateAsync(project); } else { await UpdateAsync(project.Id, project); } } } catch (Exception ex) { _logger.LogError(ex, "Erro ao salvar projeto no Qdrant"); throw; } } public async Task RemoveAsync(string id) { try { await _qdrantClient.DeleteAsync( _collectionName, new[] { new PointId { Uuid = id }.Num } ); } catch (Exception ex) { _logger.LogError(ex, "Erro ao remover projeto {Id} do Qdrant", id); throw; } } private async Task CreateProjectsCollection() { var vectorParams = new VectorParams { Size = 384, Distance = Distance.Cosine }; await _qdrantClient.CreateCollectionAsync(_collectionName, vectorParams); _logger.LogInformation("Collection de projetos '{CollectionName}' criada no Qdrant", _collectionName); } private static Project? ConvertToProject(RetrievedPoint point) { try { if (point.Payload == null) return null; return new Project { Id = point.Payload.TryGetValue("id", out var idValue) ? idValue.StringValue : point.Id.ToString(), Nome = point.Payload.TryGetValue("nome", out var nomeValue) ? nomeValue.StringValue : "", Descricao = point.Payload.TryGetValue("descricao", out var descValue) ? descValue.StringValue : "" }; } catch { return null; } } } public class QdrantScrollResult { public QdrantScrollData? result { get; set; } } public class QdrantScrollData { public QdrantPoint[]? points { get; set; } } public class QdrantPointResult { public QdrantPoint? result { get; set; } } public class QdrantPoint { public string? id { get; set; } public Dictionary? payload { get; set; } } }