using System.Net.Http.Json;
using System.Text.Json;
using ROLAC.API.Data;

namespace ROLAC.API.Services.Ai;

/// <summary>
/// Refines, translates, and maps an expense category to a Form 990 line via the Anthropic Claude
/// Messages API. It forces a single tool call (<c>tool_choice</c> → <c>map_category</c>) whose
/// <c>input_schema</c> matches our answer shape, so the model returns structured JSON in a
/// <c>tool_use</c> block. The catalog, prompt, and id validation come from
/// <see cref="ExpenseCategoryAiServiceBase"/>; this class only owns the Claude HTTP call + parse.
/// </summary>
public sealed class ClaudeExpenseCategoryAiService : ExpenseCategoryAiServiceBase
{
    private const string BaseUrl          = "https://api.anthropic.com/v1";
    private const string AnthropicVersion = "2023-06-01";

    private readonly HttpClient _http;
    private readonly IChurchAiConfigProvider _config;
    private readonly ILogger<ClaudeExpenseCategoryAiService> _logger;

    public ClaudeExpenseCategoryAiService(
        HttpClient http,
        IChurchAiConfigProvider config,
        AppDbContext db,
        ILogger<ClaudeExpenseCategoryAiService> logger)
        : base(db)
    {
        _http = http;
        _config = config;
        _logger = logger;
    }

    protected override async Task<ModelAnswer?> CallModelAsync(string prompt, CancellationToken ct)
    {
        var cfg = await _config.GetAsync(ct);
        if (string.IsNullOrWhiteSpace(cfg.ClaudeApiKey))
        {
            _logger.LogWarning("Claude API key is not configured; category AI assist is disabled.");
            return null;
        }

        try
        {
            var payload = new
            {
                model = cfg.ClaudeModel,
                max_tokens = 1024,
                tools = new[]
                {
                    new
                    {
                        name = "map_category",
                        description = "Record the refined Chinese name, English translation, and chosen Form 990 line id for the expense category.",
                        input_schema = new
                        {
                            type = "object",
                            properties = new
                            {
                                chineseName = new { type = "string" },
                                englishName = new { type = "string" },
                                form990LineId = new { type = "integer" },
                                confidence = new { type = "number" },
                            },
                            required = new[] { "chineseName", "englishName", "form990LineId", "confidence" },
                        },
                    },
                },
                tool_choice = new { type = "tool", name = "map_category" },
                messages = new[]
                {
                    new { role = "user", content = prompt },
                },
            };

            var url = $"{BaseUrl}/messages";
            using var request = new HttpRequestMessage(HttpMethod.Post, url)
            {
                Content = JsonContent.Create(payload),
            };
            request.Headers.Add("x-api-key", cfg.ClaudeApiKey);
            request.Headers.Add("anthropic-version", AnthropicVersion);

            using var response = await _http.SendAsync(request, ct);
            if (!response.IsSuccessStatusCode)
            {
                var body = await response.Content.ReadAsStringAsync(ct);
                _logger.LogWarning("Claude returned {Status}: {Body}", (int)response.StatusCode, body);
                return null;
            }

            // The forced tool call lands in content[] as a tool_use block; its `input` is our object.
            using var doc = JsonDocument.Parse(await response.Content.ReadAsStreamAsync(ct));
            foreach (var block in doc.RootElement.GetProperty("content").EnumerateArray())
            {
                if (block.GetProperty("type").GetString() != "tool_use") continue;

                var parsed = block.GetProperty("input").Deserialize<ClaudeAnswer>(
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
                if (parsed is null) return null;

                return new ModelAnswer(parsed.ChineseName, parsed.EnglishName, parsed.Form990LineId, parsed.Confidence);
            }

            _logger.LogWarning("Claude response contained no tool_use block.");
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Claude category AI assist failed.");
            return null;
        }
    }

    /// <summary>Shape of the map_category tool input the model fills in.</summary>
    private sealed class ClaudeAnswer
    {
        public string? ChineseName   { get; set; }
        public string? EnglishName   { get; set; }
        public int?    Form990LineId { get; set; }
        public double  Confidence    { get; set; }
    }
}
