using System.Net;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace Nalu.Web.Infrastructure;

public record LlmCallResult
{
    public JsonObject? Content { get; init; }
    public bool ShouldFallback { get; init; }
    public string? Error { get; init; }
}

public class GroqClient
{
    private readonly HttpClient _http;
    private readonly IConfiguration _config;
    private readonly ILogger<GroqClient> _logger;

    public GroqClient(HttpClient http, IConfiguration config, ILogger<GroqClient> logger)
    {
        _http = http;
        _config = config;
        _logger = logger;
    }

    public async Task<LlmCallResult> ChatAsync(
        string systemPrompt,
        string userMessage,
        CancellationToken ct = default)
    {
        var model = _config["Groq:Model"] ?? "llama-3.3-70b-versatile";
        var maxTokens = _config.GetValue<int>("Groq:MaxTokens", 500);
        var temperature = _config.GetValue<double>("Groq:Temperature", 0.1);

        var body = BuildRequestBody(model, systemPrompt, userMessage, maxTokens, temperature);

        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Post, "chat/completions")
            {
                Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json")
            };

            var response = await _http.SendAsync(request, ct);

            if (response.StatusCode == HttpStatusCode.TooManyRequests || (int)response.StatusCode >= 500)
            {
                _logger.LogWarning("Groq returned {Status}, triggering fallback", response.StatusCode);
                return new LlmCallResult { ShouldFallback = true };
            }

            if (!response.IsSuccessStatusCode)
            {
                var err = await response.Content.ReadAsStringAsync(ct);
                _logger.LogError("Groq error {Status}: {Body}", response.StatusCode, err);
                return new LlmCallResult { Error = $"Groq {response.StatusCode}: {err}" };
            }

            return await ParseChatResponse(response, ct);
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            _logger.LogError(ex, "Groq call failed unexpectedly");
            return new LlmCallResult { ShouldFallback = true };
        }
    }

    internal static object BuildRequestBody(
        string model,
        string systemPrompt,
        string userMessage,
        int maxTokens,
        double temperature)
    {
        return new
        {
            model,
            messages = new[]
            {
                new { role = "system", content = systemPrompt },
                new { role = "user", content = userMessage }
            },
            max_tokens = maxTokens,
            temperature,
            response_format = new { type = "json_object" }
        };
    }

    private static async Task<LlmCallResult> ParseChatResponse(HttpResponseMessage response, CancellationToken ct)
    {
        var json = await response.Content.ReadAsStringAsync(ct);
        var doc = JsonNode.Parse(json);
        var content = doc?["choices"]?[0]?["message"]?["content"]?.GetValue<string>();

        if (content is null)
            return new LlmCallResult { Error = "Empty content in Groq response" };

        var obj = JsonNode.Parse(content) as JsonObject;
        return obj is not null
            ? new LlmCallResult { Content = obj }
            : new LlmCallResult { Error = "Groq returned non-object JSON" };
    }
}
