import os

import pytest

os.environ.setdefault("USE_MOCK_LLM", "true")

from agent_framework.guardrails.calibrated import ausencia_oferta_proativa
from agent_framework.guardrails.calibrated._compat import RailResult
from agent_framework.guardrails.calibrated.prompts.ausencia_oferta_proativa import (
    build_aoferta_prompt,
)


# --------------------------------------------------------------------------- #
# Contrato de saída (2026-08-05): {allowed, reason}, reason vazio se allowed   #
# --------------------------------------------------------------------------- #
def test_prompt_aoferta_nao_pede_label():
    # O `label` nunca existiu no prompt real (só no mock); o contrato é
    # {allowed, reason}, como o FRASEOLOGIA.
    prompt = build_aoferta_prompt("Gostaria de cancelar esse serviço?")
    assert '"label"' not in prompt
    assert '"allowed": true ou false' in prompt


def test_prompt_aoferta_exige_reason_vazio_quando_allowed():
    prompt = build_aoferta_prompt("Gostaria de cancelar esse serviço?")
    assert "se allowed=true: string vazia" in prompt
    assert "se allowed=true: explicacao curta" not in prompt


def test_prompt_aoferta_injeta_texto_e_contexto():
    prompt = build_aoferta_prompt("posso cancelar?", "\nHistorico:\n[user] oi\n")
    assert "posso cancelar?" in prompt
    assert "[user] oi" in prompt


@pytest.fixture(autouse=True)
def _force_mock(monkeypatch):
    monkeypatch.setenv("USE_MOCK_LLM", "true")


def test_resposta_neutra_eh_permitida():
    r = ausencia_oferta_proativa("Posso te ajudar com mais alguma coisa?")

    assert isinstance(r, RailResult)
    assert r.allowed is True
    assert r.code == "AOFERTA"
    assert r.mechanism == "llm_supervisor"
    assert r.data is not None
    assert r.data.get("label") == "OFERTA_OK"


def test_oferta_proativa_indevida_bloqueia():
    r = ausencia_oferta_proativa(
        "Ja que esta cancelando um, quer aproveitar e cancelar os outros?",
    )

    assert r.allowed is False
    assert r.code == "AOFERTA"
    assert r.mechanism == "llm_supervisor"
    assert r.data.get("label") == "OFERTA_PROATIVA_INDEVIDA"


def test_padrao_aproveita_e_bloqueia():
    r = ausencia_oferta_proativa("Quer aproveitar e cancelar os outros?")

    assert r.allowed is False
    assert r.code == "AOFERTA"


def test_confirmacao_cancelamento_apos_nao_reconhecimento_eh_permitida():
    # Cliente nao-reconheceu os servicos em foco ("Nao contratei esses
    # servicos") e o agente confirma o cancelamento dos MESMOS itens. Per
    # classificador v9 + orquestrador, isso e action_request -> a confirmacao
    # deve ser OFERTA_OK, nao oferta proativa.
    #
    # NOTA: em USE_MOCK_LLM=true o mock decide AOFERTA por string-match no texto
    # do agente (sem gatilho de upsell -> OFERTA_OK), ignorando o contexto. Este
    # teste documenta a intencao; a validacao real do prompt e via LLM/cenarios
    # (automated_conversation_test cy0004/cy0005/cy0008).
    r = ausencia_oferta_proativa(
        "Podemos seguir com o cancelamento dos serviços VOD + Canais Abertos, "
        "no valor de R$ 19,90, e Aluguel de Filme 1, no valor de R$ 19,90, "
        "e providenciar a retirada dos valores da fatura, você confirma?",
    )

    assert r.allowed is True
    assert r.code == "AOFERTA"
    assert r.data.get("label") == "OFERTA_OK"


def test_upsell_continua_bloqueando_apos_mudanca():
    # Contraprova: a mudanca para reconhecer nao-reconhecimento NAO pode liberar
    # marcadores de upsell explicitos.
    r = ausencia_oferta_proativa(
        "Ja que esta cancelando esse, que tal tambem cancelar o outro servico?",
    )

    assert r.allowed is False
    assert r.code == "AOFERTA"
    assert r.data.get("label") == "OFERTA_PROATIVA_INDEVIDA"


def test_prompt_aoferta_confirmacao_nao_exige_repetir_justificativa():
    prompt = build_aoferta_prompt(
        "Você confirma cancelar o TIM Music?",
        "\nHistorico da conversa:\n[user] Quero cancelar o TIM Music\n",
    )
    assert "A confirmacao NAO precisa repetir a justificativa" in prompt
    assert "o pedido transacional anterior basta" in prompt


def test_prompt_aoferta_trata_canal_alternativo_como_continuidade_do_mesmo_pedido():
    prompt = build_aoferta_prompt(
        "Não consegui concluir por aqui; siga pelo canal oficial para finalizar.",
        "\nHistorico da conversa:\n[user] Quero cancelar este serviço\n",
    )
    assert "CONTINUIDADE POR CANAL ALTERNATIVO" in prompt
    assert "ESSA MESMA acao" in prompt
    assert "nao cria uma nova" in prompt
    assert "outro alvo" in prompt
