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

namespace LazyBear.MCP.Services.GitLab;

public sealed class GitLabMergeRequestTools(
    GitLabClientProvider provider,
    IConfiguration configuration,
    ToolRegistryService registry)
{
    private readonly string _token = configuration["GitLab:Token"] ?? string.Empty;
    private readonly string _baseUrl = configuration["GitLab:Url"] ?? string.Empty;
    private const string ModuleName = "GitLab";

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

    private bool TryGetClient(out RestSharp.IRestClient client, out string error)
    {
        if (provider.InitializationError is not null)
        {
            client = null!;
            error = $"GitLab клиент не инициализирован. Детали: {provider.InitializationError}";
            return false;
        }

        var clientInstance = provider.GetClient();
        if (clientInstance is null)
        {
            client = null!;
            error = "GitLab клиент не создан.";
            return false;
        }

        client = clientInstance.RestClient;
        error = string.Empty;
        return true;
    }

    private 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 $"Ошибка GitLab в tool '{toolName}'{resourcePart}: status={(int)response.StatusCode}, details={body}";
    }

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

    private static string GetVisibility(string visibility) => visibility switch
    {
        "public" => "Public",
        "internal" => "Internal",
        "private" => "Private",
        _ => visibility ?? "unknown"
    };

    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 GetState(string state) => state switch
    {
        "opened" => "Opened",
        "merged" => "Merged",
        "closed" => "Closed",
        "declined" => "Declined",
        _ => state ?? "unknown"
    };

    /// <summary>
    /// Получить список MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Получить список Merge Requests")]
    public async Task<string> ListMergeRequests(
        [Description("ID проекта")] int projectId,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("ListMergeRequests", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests", RestSharp.Method.Get);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddQueryParameter("per_page", "30");

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("list_merge_requests", response, $"/projects/{projectId}/merge_requests");
            }

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

            if (!root.TryGetProperty("merge_requests", out var mrElement) || mrElement.GetArrayLength() == 0)
            {
                return $"Merge Request в проекте #{projectId} не найдены.";
            }

            var lines = new List<string>();
            foreach (var mr in mrElement.EnumerateArray())
            {
                var iid = GetNestedString(mr, "iid") ?? "-";
                var title = GetNestedString(mr, "title") ?? "-";
                var state = GetState(GetNestedString(mr, "state") ?? "");
                var sourceBranch = GetNestedString(mr, "source", "branch") ?? "-";
                var targetBranch = GetNestedString(mr, "target", "branch") ?? "-";
                var author = GetNestedString(mr, "author", "name") ?? "-";
                var webUrl = GetNestedString(mr, "web_url") ?? "-";

                lines.Add($"#{iid} - {state}\n  {title}");
                lines.Add($"  {sourceBranch} -> {targetBranch}\n  author: {author}\n  URL: {webUrl}");
            }

            return $"Merge Requests проекта #{projectId} ({mrElement.GetArrayLength()} шт.):\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("list_merge_requests", ex);
        }
    }

    /// <summary>
    /// Получить конкретный MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Получить конкретный Merge Request")]
    public async Task<string> GetMergeRequest(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("GetMergeRequest", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Get);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("get_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
            }

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

            var iid = GetNestedString(root, "iid") ?? "-";
            var title = GetNestedString(root, "title") ?? "-";
            var state = GetState(GetNestedString(root, "state") ?? "");
            var sourceBranch = GetNestedString(root, "source", "branch") ?? "-";
            var targetBranch = GetNestedString(root, "target", "branch") ?? "-";
            var author = GetNestedString(root, "author", "name") ?? "-";
            var webUrl = GetNestedString(root, "web_url") ?? "-";
            var mergedAt = GetNestedString(root, "merged_at") ?? "-";
            var status = GetNestedString(root, "status") ?? "unknown";

            return $"Merge Request #{iid} в проекте #{projectId}:\n{title} [{state}]\n  {sourceBranch} -> {targetBranch}\n  author: {author}\n  URL: {webUrl}\n  merged_at: {mergedAt}\n  status: {status}";
        }
        catch (Exception ex)
        {
            return FormatException("get_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
        }
    }

    /// <summary>
    /// Создать Merge Request
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="title">Заголовок MR</param>
    /// <param name="sourceBranch">Имя ветки источника</param>
    /// <param name="targetBranch">Имя целевой ветки</param>
    /// <param name="description">Описание (опционально)</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Создать Merge Request")]
    public async Task<string> CreateMergeRequest(
        [Description("ID проекта")] int projectId,
        [Description("Заголовок MR")] string title,
        [Description("Имя ветки источника")] string sourceBranch,
        [Description("Имя целевой ветки")] string targetBranch,
        [Description("Описание MR")] string? description = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CreateMergeRequest", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (string.IsNullOrWhiteSpace(title))
        {
            return "Заголовок MR GitLab не может быть пустым.";
        }

        if (string.IsNullOrWhiteSpace(sourceBranch))
        {
            return "Имя ветки источника GitLab не может быть пустым.";
        }

        if (string.IsNullOrWhiteSpace(targetBranch))
        {
            return "Имя целевой ветки GitLab не может быть пустым.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests", RestSharp.Method.Post);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddHeader("Content-Type", "application/json");

            var jsonBody = new
            {
                title = title,
                source_branch = sourceBranch,
                target_branch = targetBranch,
                description = description ?? string.Empty
            }.ToJson();
            request.AddJsonBody(jsonBody);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("create_merge_request", response, $"/projects/{projectId}/merge_requests");
            }

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

            var iid = GetNestedString(root, "iid") ?? "-";
            var mrTitle = GetNestedString(root, "title") ?? "-";
            var state = GetState(GetNestedString(root, "state") ?? "");
            var webUrl = GetNestedString(root, "web_url") ?? "-";

            return $"Merge Request успешно создан в проекте #{projectId}:\nID: #{iid}\n{mrTitle} [{state}]\nURL: {webUrl}";
        }
        catch (Exception ex)
        {
            return FormatException("create_merge_request", ex);
        }
    }

    /// <summary>
    /// Закрыть MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Закрыть Merge Request")]
    public async Task<string> CloseMergeRequest(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CloseMergeRequest", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Put);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddHeader("Content-Type", "application/json");

            var jsonBody = new { state = "closed" }.ToJson();
            request.AddJsonBody(jsonBody);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("close_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
            }

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

            var mrTitle = GetNestedString(root, "title") ?? "-";

            return $"Merge Request #{mrIid} ({mrTitle}) успешно закрыт в проекте #{projectId}.";
        }
        catch (Exception ex)
        {
            return FormatException("close_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
        }
    }

    /// <summary>
    /// Открыть MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Открыть Merge Request")]
    public async Task<string> OpenMergeRequest(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("OpenMergeRequest", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}", RestSharp.Method.Put);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddHeader("Content-Type", "application/json");

            var jsonBody = new { state = "opened" }.ToJson();
            request.AddJsonBody(jsonBody);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("open_merge_request", response, $"/projects/{projectId}/merge_requests/{mrIid}");
            }

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

            var mrTitle = GetNestedString(root, "title") ?? "-";

            return $"Merge Request #{mrIid} ({mrTitle}) успешно открыт в проекте #{projectId}.";
        }
        catch (Exception ex)
        {
            return FormatException("open_merge_request", ex, $"/projects/{projectId}/merge_requests/{mrIid}");
        }
    }

    /// <summary>
    /// Получить замечания к MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Получить замечания к Merge Request")]
    public async Task<string> ListMergeRequestNotes(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("ListMergeRequestNotes", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes", RestSharp.Method.Get);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddQueryParameter("per_page", "30");

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("list_merge_request_notes", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes");
            }

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

            if (!root.TryGetProperty("notes", out var notesElement) || notesElement.GetArrayLength() == 0)
            {
                return $"Замечаний к MR #{mrIid} не найдено.";
            }

            var lines = new List<string>();
            foreach (var note in notesElement.EnumerateArray())
            {
                var author = GetNestedString(note, "author", "name") ?? "-";
                var createdAt = GetNestedString(note, "created_at") ?? "-";
                var subject = GetNestedString(note, "subject") ?? "-";
                var body = GetNestedString(note, "body") ?? "-";

                lines.Add($"Author: {author}, Time: {createdAt}\n  Subject: {subject}\n  Body: {body}");
            }

            return $"Замечания к MR #{mrIid} ({notesElement.GetArrayLength()} шт.):\n{string.Join('\n', lines)}";
        }
        catch (Exception ex)
        {
            return FormatException("list_merge_request_notes", ex);
        }
    }

    /// <summary>
    /// Добавить замечание к MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="body">Текст замечания</param>
    /// <param name="subject">Заголовок замечания (опционально)</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Добавить замечание к Merge Request")]
    public async Task<string> CreateMergeRequestNote(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        [Description("Текст замечания")] string body,
        [Description("Заголовок замечания")] string? subject = null,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("CreateMergeRequestNote", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

        if (string.IsNullOrWhiteSpace(body))
        {
            return "Текст замечания GitLab не может быть пустым.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes", RestSharp.Method.Post);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);
            request.AddHeader("Content-Type", "application/json");

            var jsonBody = new
            {
                body = body,
                subject = subject ?? string.Empty
            }.ToJson();
            request.AddJsonBody(jsonBody);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("create_merge_request_note", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes");
            }

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

            var noteId = GetNestedString(root, "id") ?? "-";
            var noteSubject = GetNestedString(root, "subject") ?? "-";
            var noteBody = GetNestedString(root, "body") ?? "-";

            return $"Замечание успешно добавлено к MR #{mrIid}:\nID: #{noteId}\n{noteSubject}\n{noteBody}";
        }
        catch (Exception ex)
        {
            return FormatException("create_merge_request_note", ex);
        }
    }

    /// <summary>
    /// Удалить замечание из MR
    /// </summary>
    /// <param name="projectId">ID проекта</param>
    /// <param name="mrIid">ID Merge Request</param>
    /// <param name="noteId">ID замечания</param>
    /// <param name="cancellationToken">Token отмены</param>
    [McpServerTool, Description("Удалить замечание из Merge Request")]
    public async Task<string> DeleteMergeRequestNote(
        [Description("ID проекта")] int projectId,
        [Description("ID Merge Request")] int mrIid,
        [Description("ID замечания")] int noteId,
        CancellationToken cancellationToken = default)
    {
        if (!TryCheckEnabled("DeleteMergeRequestNote", out var enabledError)) return enabledError;

        if (projectId <= 0)
        {
            return "ID проекта GitLab некорректно задан.";
        }

        if (mrIid <= 0)
        {
            return "ID Merge Request GitLab некорректно задан.";
        }

        if (noteId <= 0)
        {
            return "ID замечания GitLab некорректно задан.";
        }

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

        try
        {
            var request = new RestRequest($"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}", RestSharp.Method.Delete);
            request.AddHeader("Accept", "application/json");
            request.AddHeader("PRIVATE-TOKEN", _token);

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

            if (!response.IsSuccessful || string.IsNullOrWhiteSpace(response.Content))
            {
                return FormatResponseError("delete_merge_request_note", response, $"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}");
            }

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

            var noteSubject = GetNestedString(root, "subject") ?? "-";

            return $"Замечание #{noteId} ({noteSubject}) успешно удалено из MR #{mrIid}.";
        }
        catch (Exception ex)
        {
            return FormatException("delete_merge_request_note", ex, $"/projects/{projectId}/merge_requests/{mrIid}/notes/{noteId}");
        }
    }
}