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

namespace LazyBear.MCP.Services.Confluence;

[McpServerToolType]
public sealed class ConfluencePagesTools(
    ConfluenceClientProvider provider,
    IConfiguration configuration,
    ToolRegistryService registry)
{
    private readonly RestClient? _client = provider.Client;
    private readonly string? _clientInitializationError = provider.InitializationError;
    private readonly string _token = configuration["Confluence:Token"] ?? string.Empty;
    private readonly string _username = configuration["Confluence:Username"] ?? string.Empty;
    private readonly string _spaceKey = configuration["Confluence:SpaceKey"] ?? string.Empty;

    private const string ModuleName = "Confluence";

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

    [McpServerTool, Description("Получить страницу Confluence по ID")]
    public async Task<string> GetPage(
        [Description("ID страницы Confluence")] string pageId,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("GetPage", out var enabledError)) return enabledError;

        if (string.IsNullOrWhiteSpace(pageId))
        {
            return "ID страницы Confluence не задан.";
        }

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        try
        {
            var request = CreateRequest($"/wiki/api/v2/pages/{pageId}");
            request.AddQueryParameter("expand", "body.storage,version");

            var response = await client.ExecuteAsync(request, cancellationToken);

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;

            var title = GetNestedString(root, "title") ?? "-";
            var version = GetNestedLong(root, "version", "number");
            var updatedAt = GetNestedString(root, "version", "when");
            var url = GetNestedString(root, "_links", "webui");

            return $"Страница Confluence: title={title}, id={pageId}, version={version}, updated={updatedAt}, url={url}";
        }
        catch (Exception ex)
        {
            return FormatException("get_page", ex, pageId);
        }
    }

    [McpServerTool, Description("Поиск страниц Confluence по CQL запросу")]
    public async Task<string> SearchPages(
        [Description("CQL запрос. Если пусто, используется пространство по умолчанию")] string? cql = null,
        [Description("Максимум страниц в ответе")] int maxResults = 20,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("SearchPages", out var enabledError)) return enabledError;

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        var resolvedCql = ResolveCql(cql);
        if (string.IsNullOrWhiteSpace(resolvedCql))
        {
            return "CQL не задан и Confluence:SpaceKey не настроен.";
        }

        try
        {
            var request = CreateRequest("/wiki/rest/api/content/search");
            request.AddQueryParameter("cql", resolvedCql);
            request.AddQueryParameter("limit", Math.Max(1, maxResults).ToString());

            var response = await client.ExecuteAsync(request, cancellationToken);

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;

            if (!root.TryGetProperty("results", out var resultsElement) || resultsElement.GetArrayLength() == 0)
            {
                return "Страницы Confluence не найдены.";
            }

            var lines = new List<string>();
            foreach (var result in resultsElement.EnumerateArray())
            {
                var id = GetNestedString(result, "id") ?? "-";
                var title = GetNestedString(result, "title") ?? "-";
                var type = GetNestedString(result, "type") ?? "-";
                var space = GetNestedString(result, "space", "name") ?? "-";
                lines.Add($"{id}: {title} [{space}/{type}]");
            }

            return $"Страницы Confluence по CQL '{resolvedCql}':\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("search_pages", ex, resolvedCql);
        }
    }

    [McpServerTool, Description("Создать новую страницу Confluence")]
    public async Task<string> CreatePage(
        [Description("Название страницы")] string title,
        [Description("Содержимое страницы в формате storage (HTML-like WikiMarkup)")] string bodyStorage,
        [Description("Ключ пространства. Если пусто, используется Confluence:SpaceKey")] string? spaceKey = null,
        [Description("ID родительской страницы. Опционально")] string? parentId = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CreatePage", out var enabledError)) return enabledError;

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        var resolvedSpace = string.IsNullOrWhiteSpace(spaceKey) ? _spaceKey : spaceKey;
        if (string.IsNullOrWhiteSpace(resolvedSpace))
        {
            return "Пространство Confluence не задано. Укажите spaceKey или настройте Confluence:SpaceKey.";
        }

        try
        {
            var request = CreateRequest("/wiki/rest/api/content", Method.Post);

            var bodyObj = new Dictionary<string, object>
            {
                ["type"] = "page",
                ["title"] = title,
                ["space"] = new Dictionary<string, object> { ["key"] = resolvedSpace },
                ["body"] = new Dictionary<string, object>
                {
                    ["storage"] = new Dictionary<string, object>
                    {
                        ["value"] = bodyStorage,
                        ["representation"] = "storage"
                    }
                }
            };

            if (!string.IsNullOrWhiteSpace(parentId))
            {
                bodyObj["ancestors"] = new[] { new Dictionary<string, object> { ["id"] = parentId } };
            }

            request.AddJsonBody(bodyObj);

            var response = await client.ExecuteAsync(request, cancellationToken);

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;

            var createdId = GetNestedString(root, "id") ?? "-";
            var createdUrl = GetNestedString(root, "_links", "webui") ?? "-";

            return $"Страница Confluence создана: id={createdId}, title={title}, url={createdUrl}";
        }
        catch (Exception ex)
        {
            return FormatException("create_page", ex, title);
        }
    }

    [McpServerTool, Description("Обновить содержимое страницы Confluence")]
    public async Task<string> UpdatePage(
        [Description("ID страницы для обновления")] string pageId,
        [Description("Новое содержимое страницы в формате storage (HTML-like WikiMarkup)")] string bodyStorage,
        [Description("Номер текущей версии страницы")] int version,
        [Description("Новое название страницы. Если пусто, не меняется")] string? title = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("UpdatePage", out var enabledError)) return enabledError;

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        // Если title не передан — нужно получить текущий
        string resolvedTitle;
        if (!string.IsNullOrWhiteSpace(title))
        {
            resolvedTitle = title;
        }
        else
        {
            var getRequest = CreateRequest($"/wiki/rest/api/content/{pageId}");
            var getResponse = await client.ExecuteAsync(getRequest, cancellationToken);
            if (!getResponse.IsSuccessful || string.IsNullOrWhiteSpace(getResponse.Content))
            {
                return FormatResponseError("update_page_get_title", getResponse, pageId);
            }

            using var getDoc = JsonDocument.Parse(getResponse.Content);
            resolvedTitle = GetNestedString(getDoc.RootElement, "title") ?? "Untitled";
        }

        try
        {
            var request = CreateRequest($"/wiki/rest/api/content/{pageId}", Method.Put);
            request.AddJsonBody(new
            {
                version = new { number = version + 1 },
                title = resolvedTitle,
                type = "page",
                body = new
                {
                    storage = new
                    {
                        value = bodyStorage,
                        representation = "storage"
                    }
                }
            });

            var response = await client.ExecuteAsync(request, cancellationToken);

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

            return $"Страница Confluence '{pageId}' обновлена.";
        }
        catch (Exception ex)
        {
            return FormatException("update_page", ex, pageId);
        }
    }

    [McpServerTool, Description("Удалить страницу Confluence")]
    public async Task<string> DeletePage(
        [Description("ID страницы для удаления")] string pageId,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("DeletePage", out var enabledError)) return enabledError;

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        try
        {
            var request = CreateRequest($"/wiki/rest/api/content/{pageId}", Method.Delete);

            var response = await client.ExecuteAsync(request, cancellationToken);

            if (!response.IsSuccessful)
            {
                return FormatResponseError("delete_page", response, pageId);
            }

            return $"Страница Confluence '{pageId}' удалена.";
        }
        catch (Exception ex)
        {
            return FormatException("delete_page", ex, pageId);
        }
    }

    [McpServerTool, Description("Получить пространство Confluence по ключу")]
    public async Task<string> GetSpace(
        [Description("Ключ пространства")] string spaceKey,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("GetSpace", out var enabledError)) return enabledError;

        if (!TryGetClient(out var client, out var error))
        {
            return error;
        }

        try
        {
            var request = CreateRequest($"/wiki/rest/api/space/{spaceKey}");
            var response = await client.ExecuteAsync(request, cancellationToken);

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;

            var name = GetNestedString(root, "name") ?? "-";
            var key = GetNestedString(root, "key") ?? "-";
            var homepageId = GetNestedString(root, "homepage", "id");

            return $"Пространство Confluence: key={key}, name={name}, homepageId={homepageId}";
        }
        catch (Exception ex)
        {
            return FormatException("get_space", ex, spaceKey);
        }
    }

    private string? ResolveCql(string? cql)
    {
        if (!string.IsNullOrWhiteSpace(cql))
        {
            return cql;
        }

        return string.IsNullOrWhiteSpace(_spaceKey) ? null : $"space = '{_spaceKey}' ORDER BY lastmodified DESC";
    }

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

        if (!string.IsNullOrWhiteSpace(_username) && !string.IsNullOrWhiteSpace(_token))
        {
            var credentials = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_username}:{_token}"));
            request.AddHeader("Authorization", $"Basic {credentials}");
        }
        else if (!string.IsNullOrWhiteSpace(_token))
        {
            request.AddHeader("Authorization", $"Bearer {_token}");
        }

        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 = "Confluence клиент не инициализирован. Проверьте Confluence: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 long? GetNestedLong(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.Number ? current.GetInt64() : (long?)null;
    }

    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 $"Ошибка Confluence в 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 $"Ошибка Confluence в tool '{toolName}'{resourcePart}: {exception.GetType().Name}: {exception.Message}";
    }
}
