using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nalu.Web.Infrastructure;
using Nalu.Web.Models;

namespace Nalu.Web.Services;

public record LlmExtractionResult
{
    public string? ExtractedValue { get; init; }
    public bool Certain { get; init; }
    public string? Reasoning { get; init; }
    public required string Engine { get; init; }
}

public class LlmExtractionService
{
    private readonly GroqClient _groq;
    // private readonly OpenRouterClient _openRouter; // disabled — Prompt 2

    public LlmExtractionService(GroqClient groq)
    {
        _groq = groq;
        // _openRouter = openRouter;
    }

    public async Task<LlmExtractionResult> ExtractAsync(
        ValidatorDefinition validator,
        ExtractionRequest request,
        CancellationToken ct = default)
    {
        var systemPrompt = BuildSystemPrompt(validator);
        var userMessage = BuildUserMessage(request);

        var result = await _groq.ChatAsync(systemPrompt, userMessage, ct);

        // if (result.ShouldFallback)
        // {
        //     result = await _openRouter.ChatAsync(systemPrompt, userMessage, ct);
        //     engine = "llm_openrouter";
        // }

        return ParseLlmResult(result, "llm_groq");
    }

    private static string BuildSystemPrompt(ValidatorDefinition validator)
    {
        var sb = new StringBuilder();

        // Extract instructions (everything before "Diálogo:")
        var promptText = validator.Prompt;
        var dialogIdx = promptText.IndexOf("Diálogo:", StringComparison.Ordinal);

        if (dialogIdx >= 0)
        {
            sb.AppendLine(promptText[..dialogIdx].Trim());

            // Append output format instruction (after the last template placeholder)
            var ctxPlaceholder = "{{agent_context}}";
            var ctxEnd = promptText.IndexOf(ctxPlaceholder, StringComparison.Ordinal);
            if (ctxEnd >= 0)
            {
                var afterCtx = promptText[(ctxEnd + ctxPlaceholder.Length)..].Trim();
                if (!string.IsNullOrWhiteSpace(afterCtx))
                {
                    sb.AppendLine();
                    sb.AppendLine(afterCtx);
                }
            }
        }
        else
        {
            sb.AppendLine(promptText.Trim());
        }

        // Few-shot examples
        if (validator.FewShotExamples.Count > 0)
        {
            sb.AppendLine("\nExemplos:");
            foreach (var ex in validator.FewShotExamples)
            {
                sb.AppendLine("---");
                sb.AppendLine($"Agente: {ex.AgentInput}");
                sb.AppendLine($"Usuário: {ex.UserInput}");
                sb.AppendLine($"Output: {ex.Output}");
            }
            sb.AppendLine("---");
        }

        return sb.ToString().Trim();
    }

    private static string BuildUserMessage(ExtractionRequest request) =>
        $"""
        Diálogo:
        Agente: {request.AgentInput}
        Usuário: {request.UserInput}
        Contexto do agente: {request.AgentContext ?? ""}
        """;

    private static LlmExtractionResult ParseLlmResult(LlmCallResult result, string engine)
    {
        if (result.Content is null)
        {
            return new LlmExtractionResult
            {
                ExtractedValue = null,
                Certain = false,
                Reasoning = result.Error ?? "LLM call failed",
                Engine = engine
            };
        }

        try
        {
            var parsed = result.Content.Deserialize<LlmJsonResponse>(
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            if (parsed is null)
                return new LlmExtractionResult { Certain = false, Engine = engine, Reasoning = "Failed to parse LLM response" };

            // Normalize string "null" to actual null
            var extracted = parsed.ExtractedValue == "null" ? null : parsed.ExtractedValue;

            return new LlmExtractionResult
            {
                ExtractedValue = extracted,
                Certain = parsed.Certain,
                Reasoning = parsed.Reasoning,
                Engine = engine
            };
        }
        catch
        {
            return new LlmExtractionResult
            {
                Certain = false,
                Reasoning = "Could not parse LLM JSON response",
                Engine = engine
            };
        }
    }

    private record LlmJsonResponse
    {
        [JsonPropertyName("extracted_value")]
        public string? ExtractedValue { get; init; }

        [JsonPropertyName("certain")]
        public bool Certain { get; init; }

        [JsonPropertyName("reasoning")]
        public string? Reasoning { get; init; }
    }
}
