using FluentAssertions;
using Nalu.Web.Services;
using Xunit;

namespace Nalu.Tests;

public class ValidatorLoaderTests
{
    private const string FullNameMd = """
        # validate_full_name

        Extrai o nome completo do usuário.

        ## config

        - type: extraction
        - version: 1.0
        - languages: pt-BR, es-ES, en-US
        - endpoint: /v1/extract/name
        - mcp_tool: nalu_extract_name
        - mcp_description: Extrai o nome completo.

        ## deterministic_rules

        ### stop_words
        bom dia, boa tarde, boa noite, olá, oi

        ### reject_patterns
        - ^(não|nao|sei la)$
        - ^\d+$

        ### accept_patterns
        - ^meu nome é\s+(.+)$
        - ^me chamo\s+(.+)$

        ### constraints
        - min_length: 2
        - must_have_alpha: true
        - max_length: 120

        ## prompt

        Você é um extrator de nomes.

        Diálogo:
        Agente: {{agent_input}}
        Usuário: {{user_input}}

        Contexto do agente: {{agent_context}}

        Responda SOMENTE com JSON válido.

        ## few_shot_examples

        ### example 1
        - agent_input: Qual seu nome?
        - user_input: Bom dia!
        - output: {"extracted_value": null, "certain": false, "reasoning": "Saudação"}

        ### example 2
        - agent_input: Qual seu nome?
        - user_input: Maria Silva
        - output: {"extracted_value": "Maria Silva", "certain": true, "reasoning": "Nome plausível"}

        ## post_processors
        - capitalize_proper_name
        - remove_titles

        ## enrichment
        (nenhum)

        ## suggestions

        ### when_null_greeting
        {{greeting_response}} Mas preciso do seu nome. Pode me dizer?

        ### when_null_evasive
        Preciso do seu nome para continuar. Qual é?

        ### when_uncertain
        Só confirmando: seu nome é {{extracted_value}}?

        ### when_certain
        (sem sugestão — agente segue o fluxo)
        """;

    [Fact]
    public void Parse_Config_ReadsAllFields()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Id.Should().Be("validate_full_name");
        def.Type.Should().Be("extraction");
        def.Version.Should().Be("1.0");
        def.Languages.Should().BeEquivalentTo(["pt-BR", "es-ES", "en-US"]);
        def.Endpoint.Should().Be("/v1/extract/name");
        def.McpTool.Should().Be("nalu_extract_name");
        def.McpDescription.Should().Contain("Extrai o nome completo");
    }

    [Fact]
    public void Parse_StopWords_ParsesCommaSeparated()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.StopWords.Should().Contain("bom dia");
        def.StopWords.Should().Contain("boa tarde");
        def.StopWords.Should().Contain("oi");
        def.StopWords.Should().HaveCount(5);
    }

    [Fact]
    public void Parse_RejectPatterns_ParsesBulletList()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.RejectPatterns.Should().HaveCount(2);
        def.RejectPatterns[0].Should().Be(@"^(não|nao|sei la)$");
        def.RejectPatterns[1].Should().Be(@"^\d+$");
    }

    [Fact]
    public void Parse_AcceptPatterns_ParsesBulletList()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.AcceptPatterns.Should().HaveCount(2);
        def.AcceptPatterns[0].Should().Be(@"^meu nome é\s+(.+)$");
    }

    [Fact]
    public void Parse_Constraints_ParsesKeyValues()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Constraints.Should().ContainKey("min_length").WhoseValue.Should().Be("2");
        def.Constraints.Should().ContainKey("max_length").WhoseValue.Should().Be("120");
        def.Constraints.Should().ContainKey("must_have_alpha").WhoseValue.Should().Be("true");
    }

    [Fact]
    public void Parse_Prompt_CapturesFullText()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Prompt.Should().Contain("Você é um extrator de nomes");
        def.Prompt.Should().Contain("{{agent_input}}");
        def.Prompt.Should().Contain("{{user_input}}");
    }

    [Fact]
    public void Parse_FewShots_ParsesAllExamples()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.FewShotExamples.Should().HaveCount(2);
        def.FewShotExamples[0].AgentInput.Should().Be("Qual seu nome?");
        def.FewShotExamples[0].UserInput.Should().Be("Bom dia!");
        def.FewShotExamples[1].UserInput.Should().Be("Maria Silva");
    }

    [Fact]
    public void Parse_PostProcessors_ParsesBulletList()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.PostProcessors.Should().BeEquivalentTo(["capitalize_proper_name", "remove_titles"]);
    }

    [Fact]
    public void Parse_Enrichment_NenhumYieldsEmptyList()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Enrichers.Should().BeEmpty();
    }

    [Fact]
    public void Parse_Suggestions_ParsesTemplates()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Suggestions.Should().ContainKey("when_null_greeting");
        def.Suggestions.Should().ContainKey("when_null_evasive");
        def.Suggestions.Should().ContainKey("when_uncertain");
        // when_certain has "(sem sugestão..." so it should NOT be stored
        def.Suggestions.Should().NotContainKey("when_certain");
    }

    [Fact]
    public void Parse_Suggestions_ContainsPlaceholders()
    {
        var def = ValidatorLoader.ParseFromContent("validate_full_name", FullNameMd);

        def.Suggestions["when_null_greeting"].Should().Contain("{{greeting_response}}");
        def.Suggestions["when_uncertain"].Should().Contain("{{extracted_value}}");
    }
}
