using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nalu.Web.Models;
using Nalu.Web.Services.LlmRouter;

namespace Nalu.Web.Services;

public class ReplyService(ILlmRouter llmRouter, ILogger<ReplyService> logger)
{
    private const string SystemPrompt = """
        You are a conversational reply analyzer. Given an agent's message and the user's reply, determine:
        1. The type of reply (from a fixed taxonomy)
        2. What the user actually meant (considering the context of the agent's message)
        3. Whether clarification is needed
        4. A suggested follow-up message for the agent (in the language specified)

        Reply types (pick exactly one):
        - answer: User directly answered what was asked
        - question: User asked a question back
        - counter_proposal: User proposed an alternative to what was offered
        - confirmation: User confirmed positively
        - rejection: User rejected or refused
        - off_topic: User talked about something unrelated
        - greeting: User sent a greeting without useful content
        - handoff: User wants to talk to a human
        - cancel: User wants to quit or cancel
        - unclear: Ambiguous, needs clarification

        CRITICAL: When the agent mentions specific numbers (prices, quantities, installments) and the user replies with a different number, carefully analyze whether the user is:
        - Proposing a different QUANTITY (e.g., different number of installments)
        - Proposing a different AMOUNT (e.g., different price)
        - Confirming the original number
        The context of the agent's message determines the meaning of the user's number.

        value_type options: "quantity", "amount", "date", "text", "boolean" — or null if no value extracted.

        Respond ONLY in JSON format, no preamble, no markdown:
        {
          "reply_type": "string",
          "extracted_value": "string or null",
          "value_type": "string or null",
          "extracted_meaning": "string explaining what the user meant",
          "confidence": float 0-1,
          "needs_clarification": boolean,
          "suggestion_to_agent": "string or null"
        }
        """;

    public async Task<ReplyResponse> AnalyzeAsync(ReplyRequest request, CancellationToken ct = default)
    {
        var userMessage = BuildUserMessage(request);

        try
        {
            var llmResp = await llmRouter.CompleteAsync(new LlmRequest
            {
                SystemPrompt = SystemPrompt,
                UserMessage = userMessage,
                MaxTokens = 600,
                Temperature = 0.1
            }, ct);

            return ParseResponse(llmResp);
        }
        catch (ServiceUnavailableException ex)
        {
            logger.LogError(ex, "All LLM providers failed for validate_reply");
            throw;
        }
    }

    private static string BuildUserMessage(ReplyRequest r) =>
        $"""
        Language: {r.Language}

        Agent message: {r.AgentMessage}
        User reply: {r.UserReply}

        Generate suggestion_to_agent in {r.Language}.
        """;

    private static ReplyResponse ParseResponse(LlmResponse llmResp)
    {
        try
        {
            var parsed = JsonSerializer.Deserialize<LlmReplyJson>(
                llmResp.Content,
                new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

            if (parsed is null)
                return Failed();

            var replyType = ParseReplyType(parsed.ReplyType);

            return new ReplyResponse
            {
                Obtained = replyType is not null,
                ReplyType = replyType,
                ExtractedValue = parsed.ExtractedValue,
                ValueType = parsed.ValueType,
                ExtractedMeaning = parsed.ExtractedMeaning,
                Confidence = Math.Clamp(parsed.Confidence, 0.0, 1.0),
                NeedsClarification = parsed.NeedsClarification,
                SuggestionToAgent = parsed.SuggestionToAgent
            };
        }
        catch
        {
            return Failed();
        }
    }

    private static ReplyType? ParseReplyType(string? raw) => raw?.ToLowerInvariant() switch
    {
        "answer"           => Models.ReplyType.Answer,
        "question"         => Models.ReplyType.Question,
        "counter_proposal" => Models.ReplyType.CounterProposal,
        "confirmation"     => Models.ReplyType.Confirmation,
        "rejection"        => Models.ReplyType.Rejection,
        "off_topic"        => Models.ReplyType.OffTopic,
        "greeting"         => Models.ReplyType.Greeting,
        "handoff"          => Models.ReplyType.Handoff,
        "cancel"           => Models.ReplyType.Cancel,
        "unclear"          => Models.ReplyType.Unclear,
        _                  => null
    };

    private static ReplyResponse Failed() => new()
    {
        Obtained = false,
        Confidence = 0.0,
        NeedsClarification = true,
        ExtractedMeaning = "Could not analyze reply"
    };

    private record LlmReplyJson
    {
        [JsonPropertyName("reply_type")]
        public string? ReplyType { get; init; }

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

        [JsonPropertyName("value_type")]
        public string? ValueType { get; init; }

        [JsonPropertyName("extracted_meaning")]
        public string? ExtractedMeaning { get; init; }

        [JsonPropertyName("confidence")]
        public double Confidence { get; init; }

        [JsonPropertyName("needs_clarification")]
        public bool NeedsClarification { get; init; }

        [JsonPropertyName("suggestion_to_agent")]
        public string? SuggestionToAgent { get; init; }
    }
}
