from __future__ import annotations

import pytest

from app.extensions.tim_guardrails import TimOutOfScopeRail


class _FailIfCalledLLM:
    async def ainvoke(self, *args, **kwargs):
        raise AssertionError('TIM_OOS LLM must not run for structurally authorized human handoff')


class _BlockingLLM:
    def __init__(self):
        self.calls = 0

    async def ainvoke(self, *args, **kwargs):
        self.calls += 1
        return '{"allowed": false, "reason": "mensagem fora do escopo de contas/faturas TIM"}'


@pytest.mark.asyncio
async def test_tim_oos_allows_structurally_authorized_human_handoff_without_calling_llm():
    decision = await TimOutOfScopeRail().evaluate(
        'Vou encaminhar seu atendimento para uma pessoa.',
        {
            'current_route': 'human_handoff',
            'current_intent': 'human_handoff',
            'session_control': 'HUMAN_HANDOFF',
            'human_handoff_requested': True,
            'guardrail_llm': _FailIfCalledLLM(),
        },
    )

    assert decision.allowed is True
    assert decision.code == 'TIM_OOS'
    assert decision.reason == 'handoff_humano_autorizado'
    assert decision.metadata['mechanism'] == 'deterministic_handoff_bypass'


@pytest.mark.asyncio
async def test_tim_oos_allows_route_decision_structural_handoff_without_calling_llm():
    decision = await TimOutOfScopeRail().evaluate(
        'Vou encaminhar seu atendimento para uma pessoa.',
        {
            'route_decision': {
                'route': 'human_handoff',
                'intent': 'human_handoff',
                'handoff': True,
                'metadata': {'session_control': 'HUMAN_HANDOFF'},
            },
            'guardrail_llm': _FailIfCalledLLM(),
        },
    )

    assert decision.allowed is True
    assert decision.metadata['mechanism'] == 'deterministic_handoff_bypass'


@pytest.mark.asyncio
async def test_tim_oos_does_not_bypass_transfer_phrase_without_structural_handoff():
    llm = _BlockingLLM()
    decision = await TimOutOfScopeRail().evaluate(
        'Vou encaminhar seu atendimento para uma pessoa.',
        {
            'current_route': 'faturas_agent',
            'current_intent': 'contas_invoice_query',
            'guardrail_llm': llm,
        },
    )

    assert llm.calls == 1
    assert decision.allowed is False
    assert decision.reason == 'mensagem fora do escopo de contas/faturas TIM'


@pytest.mark.asyncio
async def test_tim_oos_requires_route_and_control_evidence_not_only_handoff_boolean():
    llm = _BlockingLLM()
    decision = await TimOutOfScopeRail().evaluate(
        'Vou encaminhar seu atendimento para uma pessoa.',
        {
            'handoff': True,
            'current_route': 'faturas_agent',
            'current_intent': 'contas_invoice_query',
            'guardrail_llm': llm,
        },
    )

    assert llm.calls == 1
    assert decision.allowed is False

@pytest.mark.asyncio
async def test_tim_oos_allows_terminal_handoff_from_resumed_workflow_without_router_handoff():
    decision = await TimOutOfScopeRail().evaluate(
        'Para continuar com a sua solicitação, aguarde um instante.',
        {
            'current_route': 'faturas_agent',
            'current_intent': 'contas_invoice_explanation',
            'mcp_results': [{
                'tool_name': 'retomar_workflow',
                'result': {
                    'status': 'COMPLETED',
                    'output': {
                        'session_control': 'HUMAN_HANDOFF',
                        'human_handoff_requested': True,
                        'handoff': True,
                        'session_ended': True,
                        'terminal_status': 'human_handoff',
                    },
                },
            }],
            'guardrail_llm': _FailIfCalledLLM(),
        },
    )

    assert decision.allowed is True
    assert decision.reason == 'handoff_humano_autorizado'
    assert decision.metadata['mechanism'] == 'deterministic_handoff_bypass'


@pytest.mark.asyncio
async def test_tim_oos_does_not_trust_non_terminal_workflow_handoff_flag():
    llm = _BlockingLLM()
    decision = await TimOutOfScopeRail().evaluate(
        'Vou encaminhar seu atendimento para uma pessoa.',
        {
            'current_route': 'faturas_agent',
            'current_intent': 'contas_invoice_explanation',
            'mcp_results': [{'result': {'output': {'handoff': True}}}],
            'guardrail_llm': llm,
        },
    )

    assert llm.calls == 1
    assert decision.allowed is False
