using System.ComponentModel;
using System.Text.Json;
using LazyBear.MCP.Services.ToolRegistry;
using ModelContextProtocol.Server;
using RestSharp;

namespace LazyBear.MCP.Services.Qdrant;

[McpServerToolType]
public sealed class QdrantKnowledgeTools(
    QdrantClientProvider provider,
    IConfiguration configuration,
    ToolRegistryService registry)
{
    private readonly RestClient? _client = provider.Client;
    private readonly string? _clientInitializationError = provider.InitializationError;
    private readonly string _apiKey = configuration["Qdrant:ApiKey"] ?? string.Empty;
    private readonly string _defaultCollection = configuration["Qdrant:DefaultCollection"] ?? "knowledge";

    private const string ModuleName = "Qdrant";

    private bool TryCheckEnabled(string toolName, out string error)
    {
        if (!registry.IsToolEnabled(ModuleName, toolName))
        {
            error = $"Инструмент '{toolName}' модуля Qdrant отключён в TUI.";
            return false;
        }

        error = string.Empty;
        return true;
    }

    [McpServerTool, Description("Получить список коллекций Qdrant")]
    public async Task<string> ListCollections(CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("ListCollections", out var enabledError)) return enabledError;
        if (!TryGetClient(out var client, out var error)) return error;

        try
        {
            var request = CreateRequest("/collections");
            var response = await client.ExecuteAsync(request, cancellationToken);

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("list_collections", response);
            }

            using var document = JsonDocument.Parse(response.Content);
            if (!document.RootElement.TryGetProperty("result", out var resultElement) ||
                !resultElement.TryGetProperty("collections", out var collectionsElement) ||
                collectionsElement.GetArrayLength() == 0)
            {
                return "Коллекции Qdrant не найдены.";
            }

            var names = collectionsElement
                .EnumerateArray()
                .Select(item => GetNestedString(item, "name") ?? "unknown")
                .ToArray();

            return $"Коллекции Qdrant ({names.Length} шт.): {string.Join(", ", names)}";
        }
        catch (Exception ex)
        {
            return FormatException("list_collections", ex);
        }
    }

    [McpServerTool, Description("Создать коллекцию Qdrant для базы знаний")]
    public async Task<string> CreateCollection(
        [Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
        [Description("Размер вектора") ] int vectorSize = 1536,
        [Description("Метрика расстояния: Cosine, Euclid, Dot или Manhattan")] string distance = "Cosine",
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CreateCollection", out var enabledError)) return enabledError;
        if (!TryGetClient(out var client, out var error)) return error;

        var resolvedCollection = ResolveCollection(collection);
        if (vectorSize <= 0)
            return "vectorSize должен быть больше 0.";

        if (distance is not ("Cosine" or "Euclid" or "Dot" or "Manhattan"))
            return $"distance должен быть одним из: Cosine, Euclid, Dot, Manhattan. Получено: '{distance}'.";

        try
        {
            var request = CreateRequest($"/collections/{resolvedCollection}", Method.Put);
            request.AddJsonBody(new
            {
                vectors = new
                {
                    size = vectorSize,
                    distance
                }
            });

            var response = await client.ExecuteAsync(request, cancellationToken);
            if (!response.IsSuccessful)
            {
                return FormatResponseError("create_collection", response, resolvedCollection);
            }

            return $"Коллекция Qdrant '{resolvedCollection}' создана (vectorSize={vectorSize}, distance={distance}).";
        }
        catch (Exception ex)
        {
            return FormatException("create_collection", ex, resolvedCollection);
        }
    }

    [McpServerTool, Description("Добавить или обновить документ в базе знаний Qdrant")]
    public async Task<string> UpsertKnowledgeDocument(
        [Description("ID документа/точки") ] string id,
        [Description("Вектор embedding") ] float[] vector,
        [Description("Текст/контент документа") ] string content,
        [Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
        [Description("Дополнительные метаданные JSON-объектом") ] Dictionary<string, object?>? metadata = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("UpsertKnowledgeDocument", out var enabledError)) return enabledError;
        if (!TryGetClient(out var client, out var error)) return error;

        if (string.IsNullOrWhiteSpace(id)) return "id документа не задан.";
        if (!Guid.TryParse(id, out _) && !ulong.TryParse(id, out _))
            return $"id должен быть UUID (например, 550e8400-e29b-41d4-a716-446655440000) или uint64. Получено: '{id}'.";
        if (vector is null || vector.Length == 0) return "vector не задан.";

        var resolvedCollection = ResolveCollection(collection);

        try
        {
            var payload = new Dictionary<string, object?>
            {
                ["content"] = content,
                ["updatedAt"] = DateTimeOffset.UtcNow
            };

            if (metadata is not null)
            {
                foreach (var (key, value) in metadata)
                {
                    payload[key] = value;
                }
            }

            var request = CreateRequest($"/collections/{resolvedCollection}/points", Method.Put);
            request.AddJsonBody(new
            {
                points = new object[]
                {
                    new
                    {
                        id,
                        vector,
                        payload
                    }
                }
            });

            var response = await client.ExecuteAsync(request, cancellationToken);
            if (!response.IsSuccessful)
            {
                return FormatResponseError("upsert_knowledge_document", response, resolvedCollection);
            }

            return $"Документ '{id}' сохранён в коллекции '{resolvedCollection}'.";
        }
        catch (Exception ex)
        {
            return FormatException("upsert_knowledge_document", ex, resolvedCollection);
        }
    }

    [McpServerTool, Description("Векторный поиск по базе знаний Qdrant")]
    public async Task<string> SearchKnowledge(
        [Description("Вектор запроса") ] float[] queryVector,
        [Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
        [Description("Количество результатов") ] int limit = 5,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("SearchKnowledge", out var enabledError)) return enabledError;
        if (!TryGetClient(out var client, out var error)) return error;

        if (queryVector is null || queryVector.Length == 0) return "queryVector не задан.";

        var resolvedCollection = ResolveCollection(collection);
        var resolvedLimit = Math.Max(1, limit);

        try
        {
            var request = CreateRequest($"/collections/{resolvedCollection}/points/query", Method.Post);
            request.AddJsonBody(new
            {
                query = queryVector,
                limit = resolvedLimit,
                with_payload = true
            });

            var response = await client.ExecuteAsync(request, cancellationToken);
            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("search_knowledge", response, resolvedCollection);
            }

            using var document = JsonDocument.Parse(response.Content);
            if (!document.RootElement.TryGetProperty("result", out var resultsElement) ||
                resultsElement.GetArrayLength() == 0)
            {
                return "Поиск по базе знаний не дал результатов.";
            }

            var lines = new List<string>();
            foreach (var item in resultsElement.EnumerateArray())
            {
                var pointId = item.TryGetProperty("id", out var idElement) ? idElement.ToString() : "-";
                var score = item.TryGetProperty("score", out var scoreElement)
                    ? scoreElement.GetDouble().ToString("0.####")
                    : "-";
                var content = (GetNestedString(item, "payload", "content") ?? "(без контента)")
                    .ReplaceLineEndings(" ");
                lines.Add($"id={pointId}; score={score}; content={content}");
            }

            return $"Результаты поиска Qdrant ({lines.Count}):\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("search_knowledge", ex, resolvedCollection);
        }
    }

    [McpServerTool, Description("Удалить документ из базы знаний Qdrant по ID")]
    public async Task<string> DeleteKnowledgeDocument(
        [Description("ID документа/точки") ] string id,
        [Description("Имя коллекции. Если пусто — используется Qdrant:DefaultCollection")] string? collection = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("DeleteKnowledgeDocument", out var enabledError)) return enabledError;
        if (!TryGetClient(out var client, out var error)) return error;

        if (string.IsNullOrWhiteSpace(id)) return "id документа не задан.";
        if (!Guid.TryParse(id, out _) && !ulong.TryParse(id, out _))
            return $"id должен быть UUID (например, 550e8400-e29b-41d4-a716-446655440000) или uint64. Получено: '{id}'.";

        var resolvedCollection = ResolveCollection(collection);

        try
        {
            var request = CreateRequest($"/collections/{resolvedCollection}/points/delete", Method.Post);
            request.AddJsonBody(new
            {
                points = new[] { id }
            });

            var response = await client.ExecuteAsync(request, cancellationToken);
            if (!response.IsSuccessful)
            {
                return FormatResponseError("delete_knowledge_document", response, resolvedCollection);
            }

            return $"Документ '{id}' удалён из коллекции '{resolvedCollection}'.";
        }
        catch (Exception ex)
        {
            return FormatException("delete_knowledge_document", ex, resolvedCollection);
        }
    }

    private string ResolveCollection(string? collection)
    {
        return string.IsNullOrWhiteSpace(collection) ? _defaultCollection : collection;
    }

    private RestRequest CreateRequest(string resource, Method method = Method.Get)
    {
        var request = new RestRequest(resource, method);
        request.AddHeader("Accept", "application/json");

        if (!string.IsNullOrWhiteSpace(_apiKey))
        {
            request.AddHeader("api-key", _apiKey);
        }

        return request;
    }

    private bool TryGetClient(out RestClient client, out string error)
    {
        if (_client is null)
        {
            client = null!;
            var details = string.IsNullOrWhiteSpace(_clientInitializationError)
                ? string.Empty
                : $" Детали: {_clientInitializationError}";
            error = "Qdrant клиент не инициализирован. Проверьте Qdrant:Url." + details;
            return false;
        }

        client = _client;
        error = string.Empty;
        return true;
    }

    private static string? GetNestedString(JsonElement element, params string[] path)
    {
        var current = element;
        foreach (var segment in path)
        {
            if (!current.TryGetProperty(segment, out current))
            {
                return null;
            }
        }

        return current.ValueKind == JsonValueKind.String ? current.GetString() : current.ToString();
    }

    private static string FormatResponseError(string toolName, RestResponse response, string? resource = null)
    {
        var resourcePart = string.IsNullOrWhiteSpace(resource) ? string.Empty : $", resource='{resource}'";
        var body = string.IsNullOrWhiteSpace(response.Content) ? "-" : response.Content;
        return $"Ошибка Qdrant в tool '{toolName}'{resourcePart}: status={(int)response.StatusCode}, details={body}";
    }

    private static string FormatException(string toolName, Exception exception, string? resource = null)
    {
        var resourcePart = string.IsNullOrWhiteSpace(resource) ? string.Empty : $", resource='{resource}'";
        return $"Ошибка Qdrant в tool '{toolName}'{resourcePart}: {exception.GetType().Name}: {exception.Message}";
    }
}
