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

namespace LazyBear.MCP.Services.Jira;

[McpServerToolType]
public sealed class JiraIssueTools(
    JiraClientProvider provider,
    IConfiguration configuration,
    ToolRegistryService registry)
{
    private readonly RestClient? _client = provider.Client;
    private readonly string? _clientInitializationError = provider.InitializationError;
    private readonly string _token = configuration["Jira:Token"] ?? string.Empty;
    private readonly string _defaultProject = configuration["Jira:Project"] ?? string.Empty;

    private const string ModuleName = "Jira";

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

    [McpServerTool, Description("Получить задачу Jira по ключу")]
    public async Task<string> GetIssue(
        [Description("Ключ задачи, например PROJ-123")] string issueKey,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("GetIssue", out var enabledError)) return enabledError;

        if (string.IsNullOrWhiteSpace(issueKey))
        {
            return "Ключ задачи Jira не задан.";
        }

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

        try
        {
            var request = CreateRequest($"/rest/api/3/issue/{issueKey}");
            var response = await client.ExecuteAsync(request, cancellationToken);

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

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

            var summary = GetNestedString(root, "fields", "summary") ?? "-";
            var status = GetNestedString(root, "fields", "status", "name") ?? "-";
            var issueType = GetNestedString(root, "fields", "issuetype", "name") ?? "-";
            var assignee = GetNestedString(root, "fields", "assignee", "displayName") ?? "unassigned";

            return $"Задача '{issueKey}': summary={summary}, status={status}, type={issueType}, assignee={assignee}";
        }
        catch (Exception ex)
        {
            return FormatException("get_issue", ex, issueKey);
        }
    }

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

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

        var resolvedJql = ResolveJql(jql);
        if (string.IsNullOrWhiteSpace(resolvedJql))
        {
            return "JQL не задан и Jira:Project не настроен.";
        }

        try
        {
            var request = CreateRequest("/rest/api/3/search/jql");
            request.AddQueryParameter("jql", resolvedJql);
            request.AddQueryParameter("maxResults", Math.Max(1, maxResults).ToString());
            request.AddQueryParameter("fields", "summary,status,issuetype");

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

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

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

            if (!root.TryGetProperty("issues", out var issuesElement) || issuesElement.GetArrayLength() == 0)
            {
                return "Задачи Jira не найдены.";
            }

            var lines = new List<string>();
            foreach (var issue in issuesElement.EnumerateArray())
            {
                var key = issue.TryGetProperty("key", out var keyElement) ? keyElement.GetString() ?? "unknown" : "unknown";
                var summary = GetNestedString(issue, "fields", "summary") ?? "-";
                var status = GetNestedString(issue, "fields", "status", "name") ?? "-";
                lines.Add($"{key}: {summary} [{status}]");
            }

            return $"Задачи Jira по JQL '{resolvedJql}':\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("list_issues", ex, resolvedJql);
        }
    }

    [McpServerTool, Description("Создать задачу Jira")]
    public async Task<string> CreateIssue(
        [Description("Summary задачи")] string summary,
        [Description("Ключ проекта. Если пусто, используется Jira:Project")] string? projectKey = null,
        [Description("Тип задачи, например Task или Bug")] string issueType = "Task",
        [Description("Описание задачи")] string? description = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CreateIssue", out var enabledError)) return enabledError;

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

        var resolvedProject = string.IsNullOrWhiteSpace(projectKey) ? _defaultProject : projectKey;
        if (string.IsNullOrWhiteSpace(resolvedProject))
        {
            return "Проект Jira не задан. Укажите projectKey или настройте Jira:Project.";
        }

        try
        {
            var request = CreateRequest("/rest/api/3/issue", Method.Post);
            request.AddJsonBody(new
            {
                fields = new
                {
                    project = new { key = resolvedProject },
                    summary,
                    issuetype = new { name = issueType },
                    description = string.IsNullOrWhiteSpace(description)
                        ? null
                        : new
                        {
                            type = "doc",
                            version = 1,
                            content = new object[]
                            {
                                new
                                {
                                    type = "paragraph",
                                    content = new object[]
                                    {
                                        new
                                        {
                                            type = "text",
                                            text = description
                                        }
                                    }
                                }
                            }
                        }
                }
            });

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

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;
            var createdKey = root.TryGetProperty("key", out var keyElement) ? keyElement.GetString() ?? "-" : "-";

            return $"Задача Jira создана: {createdKey}";
        }
        catch (Exception ex)
        {
            return FormatException("create_issue", ex, resolvedProject);
        }
    }

    [McpServerTool, Description("Обновить summary или описание задачи Jira")]
    public async Task<string> UpdateIssue(
        [Description("Ключ задачи")] string issueKey,
        [Description("Новый summary")] string? summary = null,
        [Description("Новое описание")] string? description = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("UpdateIssue", out var enabledError)) return enabledError;

        if (string.IsNullOrWhiteSpace(summary) && string.IsNullOrWhiteSpace(description))
        {
            return $"Нет полей для обновления задачи '{issueKey}'.";
        }

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

        try
        {
            var fields = new Dictionary<string, object?>();

            if (!string.IsNullOrWhiteSpace(summary))
            {
                fields["summary"] = summary;
            }

            if (!string.IsNullOrWhiteSpace(description))
            {
                fields["description"] = new
                {
                    type = "doc",
                    version = 1,
                    content = new object[]
                    {
                        new
                        {
                            type = "paragraph",
                            content = new object[]
                            {
                                new
                                {
                                    type = "text",
                                    text = description
                                }
                            }
                        }
                    }
                };
            }

            var request = CreateRequest($"/rest/api/3/issue/{issueKey}", Method.Put);
            request.AddJsonBody(new { fields });

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

            if (!response.IsSuccessful)
            {
                return FormatResponseError("update_issue", response, issueKey);
            }

            return $"Задача Jira '{issueKey}' обновлена.";
        }
        catch (Exception ex)
        {
            return FormatException("update_issue", ex, issueKey);
        }
    }

    [McpServerTool, Description("Доступные переходы статуса для задачи Jira")]
    public async Task<string> GetIssueStatuses(
        [Description("Ключ задачи")] string issueKey,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("GetIssueStatuses", out var enabledError)) return enabledError;

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

        try
        {
            var request = CreateRequest($"/rest/api/3/issue/{issueKey}/transitions");
            var response = await client.ExecuteAsync(request, cancellationToken);

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

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

            if (!root.TryGetProperty("transitions", out var transitionsElement) || transitionsElement.GetArrayLength() == 0)
            {
                return $"Для задачи '{issueKey}' нет доступных переходов.";
            }

            var lines = new List<string>();
            foreach (var transition in transitionsElement.EnumerateArray())
            {
                var name = transition.TryGetProperty("name", out var nameElement) ? nameElement.GetString() ?? "-" : "-";
                var targetStatus = GetNestedString(transition, "to", "name") ?? "-";
                lines.Add($"{name} -> {targetStatus}");
            }

            return $"Переходы статуса для '{issueKey}':\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("get_issue_statuses", ex, issueKey);
        }
    }

    [McpServerTool, Description("Список комментариев задачи Jira")]
    public async Task<string> ListIssueComments(
        [Description("Ключ задачи")] string issueKey,
        [Description("Максимум комментариев")] int limit = 20,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("ListIssueComments", out var enabledError)) return enabledError;

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

        try
        {
            var request = CreateRequest($"/rest/api/3/issue/{issueKey}/comment");
            request.AddQueryParameter("maxResults", Math.Max(1, limit).ToString());

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

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

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

            if (!root.TryGetProperty("comments", out var commentsElement) || commentsElement.GetArrayLength() == 0)
            {
                return $"Комментарии к задаче '{issueKey}' отсутствуют.";
            }

            var lines = new List<string>();
            foreach (var comment in commentsElement.EnumerateArray())
            {
                var id = comment.TryGetProperty("id", out var idElement) ? idElement.GetString() ?? "-" : "-";
                var author = GetNestedString(comment, "author", "displayName") ?? "unknown";
                var text = ExtractCommentText(comment.GetProperty("body"));
                lines.Add($"{id}: {author} -> {text}");
            }

            return $"Комментарии задачи '{issueKey}':\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("list_issue_comments", ex, issueKey);
        }
    }

    [McpServerTool, Description("Добавить комментарий к задаче Jira")]
    public async Task<string> AddComment(
        [Description("Ключ задачи")] string issueKey,
        [Description("Текст комментария")] string body,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("AddComment", out var enabledError)) return enabledError;

        if (string.IsNullOrWhiteSpace(body))
        {
            return "Текст комментария Jira не задан.";
        }

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

        try
        {
            var request = CreateRequest($"/rest/api/3/issue/{issueKey}/comment", Method.Post);
            request.AddJsonBody(new
            {
                body = new
                {
                    type = "doc",
                    version = 1,
                    content = new object[]
                    {
                        new
                        {
                            type = "paragraph",
                            content = new object[]
                            {
                                new
                                {
                                    type = "text",
                                    text = body
                                }
                            }
                        }
                    }
                }
            });

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

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

            using var document = JsonDocument.Parse(response.Content);
            var root = document.RootElement;
            var commentId = root.TryGetProperty("id", out var idElement) ? idElement.GetString() ?? "-" : "-";

            return $"Комментарий добавлен к задаче '{issueKey}', id={commentId}.";
        }
        catch (Exception ex)
        {
            return FormatException("add_comment", ex, issueKey);
        }
    }

    private string? ResolveJql(string? jql)
    {
        if (!string.IsNullOrWhiteSpace(jql))
        {
            return jql;
        }

        return string.IsNullOrWhiteSpace(_defaultProject) ? null : $"project = {_defaultProject} ORDER BY updated DESC";
    }

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

        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 = "Jira клиент не инициализирован. Проверьте Jira: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 ExtractCommentText(JsonElement body)
    {
        var chunks = new List<string>();
        CollectText(body, chunks);
        return chunks.Count == 0 ? "-" : string.Join(" ", chunks);
    }

    private static void CollectText(JsonElement element, List<string> chunks)
    {
        if (element.ValueKind == JsonValueKind.Object)
        {
            if (element.TryGetProperty("text", out var textElement) && textElement.ValueKind == JsonValueKind.String)
            {
                chunks.Add(textElement.GetString() ?? string.Empty);
            }

            foreach (var property in element.EnumerateObject())
            {
                CollectText(property.Value, chunks);
            }

            return;
        }

        if (element.ValueKind == JsonValueKind.Array)
        {
            foreach (var item in element.EnumerateArray())
            {
                CollectText(item, chunks);
            }
        }
    }

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