using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace WebClientTpos.Server.AiChat;

/// <summary>
/// EN: AI chat provider for Anthropic Claude API (Messages API v1).
///     Handles Claude-specific request/response format and tool_use content blocks.
/// VI: AI chat provider cho Anthropic Claude API (Messages API v1).
///     Xu ly dinh dang request/response rieng cua Claude va tool_use content blocks.
/// </summary>
public class ClaudeChatProvider : IAiChatProvider
{
    private const string AnthropicApiUrl = "https://api.anthropic.com/v1/messages";
    private const string AnthropicVersion = "2023-06-01";
    private const int DefaultMaxTokens = 4096;

    private readonly HttpClient _httpClient;
    private readonly ILogger<ClaudeChatProvider> _logger;

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        PropertyNameCaseInsensitive = true
    };

    public ClaudeChatProvider(HttpClient httpClient, ILogger<ClaudeChatProvider> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    /// <summary>
    /// EN: Send a chat completion request to the Anthropic Claude API.
    /// VI: Gui chat completion request den Anthropic Claude API.
    /// </summary>
    public async Task<AiChatResponse> SendAsync(AiChatRequest request, CancellationToken cancellationToken = default)
    {
        var messages = BuildMessages(request.Messages);
        var tools = BuildTools(request.Tools);

        var body = new Dictionary<string, object>
        {
            ["model"] = request.Model,
            ["max_tokens"] = DefaultMaxTokens,
            ["messages"] = messages
        };

        if (!string.IsNullOrWhiteSpace(request.SystemPrompt))
            body["system"] = request.SystemPrompt;

        if (tools is not null)
            body["tools"] = tools;

        var json = JsonSerializer.Serialize(body, JsonOptions);

        using var httpRequest = new HttpRequestMessage(HttpMethod.Post, AnthropicApiUrl);
        httpRequest.Content = new StringContent(json, Encoding.UTF8, "application/json");
        httpRequest.Headers.TryAddWithoutValidation("x-api-key", request.ApiKey);
        httpRequest.Headers.TryAddWithoutValidation("anthropic-version", AnthropicVersion);

        _logger.LogDebug("EN: Sending Claude chat request with model {Model}.", request.Model);

        using var response = await _httpClient.SendAsync(httpRequest, cancellationToken);
        var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);

        if (!response.IsSuccessStatusCode)
        {
            _logger.LogError("EN: Claude API error {StatusCode}: {Body}.",
                             (int)response.StatusCode, responseBody);
            throw new HttpRequestException(
                $"Claude API returned {(int)response.StatusCode}: {responseBody}");
        }

        return ParseResponse(responseBody);
    }

    /// <summary>
    /// EN: Build the messages array for the Claude API request.
    ///     Claude does not use a "system" role in messages — it is a top-level field.
    /// VI: Xay dung mang messages cho Claude API request.
    ///     Claude khong dung role "system" trong messages — no la truong cap cao nhat.
    /// </summary>
    private static List<object> BuildMessages(List<AiChatMessage> messages)
    {
        var result = new List<object>();

        foreach (var msg in messages)
        {
            // EN: Skip system messages — handled as top-level "system" field
            // VI: Bo qua system messages — xu ly o truong "system" cap cao nhat
            if (msg.Role == "system")
                continue;

            if (msg.ToolCalls is { Count: > 0 })
            {
                // EN: Assistant message with tool_use content blocks
                // VI: Tin nhan assistant voi tool_use content blocks
                var content = new List<object>();

                if (!string.IsNullOrEmpty(msg.Content))
                {
                    content.Add(new { type = "text", text = msg.Content });
                }

                foreach (var tc in msg.ToolCalls)
                {
                    content.Add(new
                    {
                        type = "tool_use",
                        id = tc.Id,
                        name = tc.Name,
                        input = JsonSerializer.Deserialize<JsonElement>(tc.ArgumentsJson)
                    });
                }

                result.Add(new { role = "assistant", content });
            }
            else if (msg.ToolCallId is not null)
            {
                // EN: Tool result message — Claude uses "user" role with tool_result content block
                // VI: Tin nhan ket qua tool — Claude dung role "user" voi tool_result content block
                result.Add(new
                {
                    role = "user",
                    content = new object[]
                    {
                        new
                        {
                            type = "tool_result",
                            tool_use_id = msg.ToolCallId,
                            content = msg.Content
                        }
                    }
                });
            }
            else
            {
                result.Add(new { role = msg.Role, content = msg.Content });
            }
        }

        return result;
    }

    /// <summary>
    /// EN: Build the tools array in Claude's tool format (name, description, input_schema).
    /// VI: Xay dung mang tools theo dinh dang tool cua Claude (name, description, input_schema).
    /// </summary>
    private static List<object>? BuildTools(List<AiToolDefinition>? tools)
    {
        if (tools is null or { Count: 0 })
            return null;

        return tools.Select(t => (object)new
        {
            name = t.Name,
            description = t.Description,
            input_schema = t.Parameters
        }).ToList();
    }

    /// <summary>
    /// EN: Parse the Claude API response. Handles text and tool_use content blocks.
    /// VI: Phan tich Claude API response. Xu ly text va tool_use content blocks.
    /// </summary>
    private AiChatResponse ParseResponse(string responseBody)
    {
        using var doc = JsonDocument.Parse(responseBody);
        var root = doc.RootElement;

        var contentBlocks = root.GetProperty("content");
        var textParts = new List<string>();
        var toolCalls = new List<AiToolCall>();

        foreach (var block in contentBlocks.EnumerateArray())
        {
            var type = block.GetProperty("type").GetString();

            switch (type)
            {
                case "text":
                    var text = block.GetProperty("text").GetString();
                    if (!string.IsNullOrEmpty(text))
                        textParts.Add(text);
                    break;

                case "tool_use":
                    var id = block.GetProperty("id").GetString()!;
                    var name = block.GetProperty("name").GetString()!;
                    var input = block.GetProperty("input").GetRawText();
                    toolCalls.Add(new AiToolCall(id, name, input));
                    break;
            }
        }

        var content = string.Join("\n", textParts);
        var hasToolCalls = toolCalls.Count > 0;

        _logger.LogDebug("EN: Claude response parsed — content length: {ContentLen}, tool calls: {ToolCallCount}.",
                         content.Length, toolCalls.Count);

        return new AiChatResponse(
            content,
            hasToolCalls ? toolCalls : null,
            hasToolCalls);
    }
}
