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

namespace ROLAC.API.Services.Ai;

/// <summary>
/// Translates and classifies an expense via the Google Gemini <c>generateContent</c> API, using
/// Gemini's structured-output mode (<c>responseSchema</c>). The catalog, prompt, and id validation
/// come from <see cref="ExpenseAiServiceBase"/>; this class only owns the Gemini HTTP call + parse.
/// </summary>
public sealed class GeminiExpenseAiService : ExpenseAiServiceBase
{
    private const string BaseUrl = "https://generativelanguage.googleapis.com/v1beta";

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

    public GeminiExpenseAiService(
        HttpClient http,
        IChurchAiConfigProvider config,
        AppDbContext db,
        ILogger<GeminiExpenseAiService> 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.GeminiApiKey))
        {
            _logger.LogWarning("Gemini API key is not configured; expense AI assist is disabled.");
            return null;
        }

        try
        {
            var payload = new
            {
                contents = new[]
                {
                    new { parts = new[] { new { text = prompt } } },
                },
                generationConfig = new
                {
                    responseMimeType = "application/json",
                    responseSchema = new
                    {
                        type = "object",
                        properties = new
                        {
                            chineseDescription = new { type = "string" },
                            englishDescription = new { type = "string" },
                            groupId = new { type = "integer" },
                            subCategoryId = new { type = "integer" },
                            confidence = new { type = "number" },
                        },
                        required = new[] { "chineseDescription", "englishDescription", "groupId", "subCategoryId", "confidence" },
                    },
                },
            };

            var url = $"{BaseUrl}/models/{cfg.GeminiModel}:generateContent";
            using var request = new HttpRequestMessage(HttpMethod.Post, url)
            {
                Content = JsonContent.Create(payload),
            };
            request.Headers.Add("X-goog-api-key", cfg.GeminiApiKey);

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

            // Navigate candidates[0].content.parts[0].text — the model's JSON answer as a string.
            using var doc = JsonDocument.Parse(await response.Content.ReadAsStreamAsync(ct));
            var text = doc.RootElement
                .GetProperty("candidates")[0]
                .GetProperty("content")
                .GetProperty("parts")[0]
                .GetProperty("text")
                .GetString();

            if (string.IsNullOrWhiteSpace(text))
            {
                _logger.LogWarning("Gemini response contained no text part.");
                return null;
            }

            var parsed = JsonSerializer.Deserialize<GeminiAnswer>(
                text, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            if (parsed is null) return null;

            return new ModelAnswer(parsed.EnglishDescription, parsed.ChineseDescription, parsed.GroupId, parsed.SubCategoryId, parsed.Confidence);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Gemini expense AI assist failed.");
            return null;
        }
    }

    /// <summary>Shape of Gemini's JSON answer (constrained by responseSchema).</summary>
    private sealed class GeminiAnswer
    {
        public string? EnglishDescription { get; set; }
        public string? ChineseDescription { get; set; }
        public int     GroupId            { get; set; }
        public int     SubCategoryId      { get; set; }
        public double  Confidence         { get; set; }
    }
}
