from app.domain.contas.conversation_policy import evaluate, is_regulatory_threat


def test_no_match_three_consecutive_then_terminal(monkeypatch):
    monkeypatch.setenv("CONTAS_NO_MATCH_MAX_CONSECUTIVE", "3")
    base = {"user_text": "???", "intent": "contas_no_match", "route": "suporte_contas_agent", "route_decision": {"method": "llm", "intent": "contas_no_match"}}
    d1 = evaluate(base)
    assert d1 and d1.patch["no_match_count"] == 1 and d1.route == "conversation_policy_response"
    d2 = evaluate({**base, "no_match_count": 1})
    assert d2 and d2.patch["no_match_count"] == 2 and not d2.patch.get("session_ended")
    d3 = evaluate({**base, "no_match_count": 2})
    assert d3 and d3.patch["no_match_count"] == 3
    assert d3.route == "end_session"
    assert d3.patch["terminal_status"] == "erro_no_match"


def test_understood_turn_resets_no_match_counter():
    state = {"user_text": "quero minha fatura", "intent": "contas_invoice_query", "route": "faturas_agent", "route_decision": {"method": "keyword", "intent": "contas_invoice_query"}, "no_match_count": 2}
    d = evaluate(state)
    assert d and d.patch == {"no_match_count": 0}


def test_regulatory_threat_without_focus_does_not_invent_transaction():
    state = {"user_text": "vou procurar a Anatel e o Procon", "intent": "fallback", "route": "faturas_agent", "route_decision": {"method": "llm", "intent": "fallback"}}
    d = evaluate(state)
    assert d and d.route == "suporte_contas_agent"
    assert d.intent == "contas_regulatory_complaint"
    assert d.patch["mcp_tools"] == []


def test_regulatory_threat_with_focused_subject_preserves_action_context():
    state = {
        "user_text": "se não resolver vou para a Anatel",
        "intent": "fallback", "route": "faturas_agent", "route_decision": {"method": "llm", "intent": "fallback"},
        "last_transaction": {"arguments": {"subject": "HBO Max Standard"}},
    }
    d = evaluate(state)
    assert d and d.route == "contestacao_agent"
    assert d.intent == "contas_vas_cancel"
    assert d.patch["regulatory_context"]["focused_items"] == ["HBO Max Standard"]


def test_regulatory_detector_terms():
    assert is_regulatory_threat("vou procurar meus direitos no Procon")
    assert is_regulatory_threat("vou à justiça")


def test_retention_offer_and_two_step_acceptance(monkeypatch):
    import app.domain.contas.conversation_policy as policy
    items = [{"name": "VAS A", "msisdn": "11999999999"}, {"name": "VAS B", "msisdn": "11999999999"}]
    monkeypatch.setattr(policy, "_retention_items", lambda state: items)
    first = policy.evaluate({"user_text": "quero um atendente", "route": "human_handoff", "intent": "human_handoff", "route_decision": {"method": "continuity", "intent": "human_handoff"}})
    assert first and first.route == "conversation_policy_response"
    assert first.patch["contas_retention"]["pending_stage"] == "explain"

    second = policy.evaluate({"user_text": "não, quero atendente", "route": "human_handoff", "intent": "human_handoff", "route_decision": {"method": "continuity", "intent": "human_handoff"}, "contas_retention": first.patch["contas_retention"]})
    assert second and second.patch["contas_retention"]["pending_stage"] == "continue"

    third = policy.evaluate({"user_text": "sim", "route": "human_handoff", "intent": "human_handoff", "route_decision": {"method": "continuity", "intent": "human_handoff"}, "contas_retention": second.patch["contas_retention"]})
    assert third and third.route == "contestacao_agent"
    assert third.patch["context"]["focused_items"] == ["VAS A", "VAS B"]
    assert third.patch["_route_metadata"]["original_input"] == "cancelar VAS A e VAS B"

def test_retention_second_decline_handoffs(monkeypatch):
    import app.domain.contas.conversation_policy as policy
    state = {"user_text": "não, atendente", "route": "human_handoff", "intent": "human_handoff", "route_decision": {"method": "continuity", "intent": "human_handoff"}, "contas_retention": {"pending_stage": "continue", "stages_offered": ["explain", "continue"], "items": [{"name": "VAS A", "msisdn": "11999999999"}]}}
    d = policy.evaluate(state)
    assert d and d.route == "human_handoff" and d.reason == "human_retention_declined"


def test_generic_router_fallback_is_not_counted_as_incomprehensible():
    d = evaluate({"user_text": "quero falar sobre outra empresa", "intent": "fallback", "route": "faturas_agent", "route_decision": {"method": "fallback", "intent": "fallback"}, "no_match_count": 1})
    assert d and d.patch == {"no_match_count": 0}


def test_explicit_gratitude_closes_only_without_live_transaction():
    history = [{"role": "assistant", "content": "Com essa explicação, sanei sua dúvida?", "metadata": {"intent": "contas_invoice_explanation"}}]
    d = evaluate({"user_text": "entendi, obrigado, era só isso", "intent": "fallback", "route": "suporte_contas_agent", "history": history})
    assert d and d.route == "end_session"
    assert d.patch["terminal_status"] == "resolvido"

    active = evaluate({
        "user_text": "entendi, obrigado, era só isso",
        "intent": "contas_vas_cancel",
        "route": "contestacao_agent",
        "history": history,
        "transaction_status": "AWAITING_CONFIRMATION",
        "active_transaction": {"tool_name": "cancelar_vas_avulso", "status": "AWAITING_CONFIRMATION"},
    })
    assert active is None or active.route != "end_session"


def test_plural_confirmation_after_invoice_explanation_stays_in_explanation():
    history = [{
        "role": "assistant",
        "content": "Essas duas cobranças de VOD + Canais abertos são as que você não reconhece.",
        "metadata": {"intent": "contas_invoice_explanation"},
    }]
    d = evaluate({
        "user_text": "as duas mesmo, pode seguir",
        "intent": "fallback",
        "route": "suporte_contas_agent",
        "history": history,
    })
    assert d and d.route == "faturas_agent"
    assert d.intent == "contas_invoice_explanation"
    assert d.patch["mcp_tools"] == ["invoice_explanation"]


def test_explicit_closure_has_precedence_over_no_match():
    d = evaluate({
        "user_text": "entendi, era só isso mesmo, obrigado",
        "intent": "contas_no_match",
        "route": "suporte_contas_agent",
        "route_decision": {"method": "llm", "intent": "contas_no_match"},
        "no_match_count": 1,
    })
    assert d and d.route == "end_session"
    assert d.intent == "contas_conversation_close"
    assert d.reason == "explicit_customer_closure"
    assert d.patch["terminal_status"] == "resolvido"
    assert d.patch["no_match_count"] == 0


def test_explicit_closure_does_not_preempt_live_confirmation_even_when_router_says_no_match():
    d = evaluate({
        "user_text": "entendi, era só isso mesmo, obrigado",
        "intent": "contas_no_match",
        "route": "contestacao_agent",
        "route_decision": {"method": "llm", "intent": "contas_no_match"},
        "transaction_status": "AWAITING_CONFIRMATION",
        "active_transaction": {"tool_name": "cancelar_vas_avulso", "status": "AWAITING_CONFIRMATION"},
    })
    assert d and d.route == "conversation_policy_response"
    assert d.intent == "contas_no_match_retry"
