"""Tests for workflow routing and LLM boundary helpers."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

import pytest

from src.schemas import ClassificationResult, DraftResponseResult, WorkflowState
from src.workflow import (
    classify_intent,
    decide_route_and_escalation,
    determine_escalation,
    draft_response,
    fetch_context,
    run_workflow,
)


@dataclass
class FakeStructuredLLM:
    """Return a predetermined structured result."""

    result: Any

    def invoke(self, _: list[tuple[str, str]]) -> Any:
        """Return the configured result for the test."""
        return self.result


class FakeLLM:
    """Mimic the structured-output surface used by the workflow."""

    def __init__(self, results: list[Any]) -> None:
        """Store the queued structured outputs."""
        self._results = results

    def with_structured_output(self, _: Any) -> FakeStructuredLLM:
        """Return the next fake structured output wrapper."""
        return FakeStructuredLLM(self._results.pop(0))


def test_fetch_context_raises_without_intent() -> None:
    """Reject context fetching before classification."""
    state = WorkflowState(ticket_text="Need help", metadata={"ticket_id": "T-1"})

    with pytest.raises(ValueError):
        fetch_context(state)


def test_determine_escalation_covers_key_rules() -> None:
    """Escalate risky billing and account cases with explicit rules."""
    assert determine_escalation(
        "I was charged twice for my order",
        "billing_issue",
        {"order_id": "1234"},
    )
    assert determine_escalation(
        "I cannot log in and think the account was hacked",
        "account_access",
        {"account_id": "A-102"},
    )
    assert not determine_escalation(
        "Where is my order?",
        "order_status",
        {"order_id": "2345"},
    )
    assert determine_escalation(
        "I want a refund outside the normal window.",
        "refund_request",
        {"order_id": "1234"},
    )


def test_decide_route_and_escalation_promotes_to_escalations() -> None:
    """Move high-risk tickets to the escalation route."""
    state = WorkflowState(
        ticket_text="My blender arrived with broken glass and feels unsafe",
        metadata={"ticket_id": "T-2", "order_id": "6789"},
        intent="damaged_item",
    )

    updated = decide_route_and_escalation(state)

    assert updated.route == "escalations"
    assert updated.should_escalate is True


def test_decide_route_and_escalation_keeps_standard_route() -> None:
    """Keep the normal route for straightforward low-risk tickets."""
    state = WorkflowState(
        ticket_text="Please cancel my subscription before renewal.",
        metadata={"ticket_id": "T-2", "account_id": "A-103"},
        intent="subscription_cancellation",
    )

    updated = decide_route_and_escalation(state)

    assert updated.route == "retention"
    assert updated.should_escalate is False


def test_fetch_context_includes_account_lookup() -> None:
    """Fetch account status for account-related intents."""
    state = WorkflowState(
        ticket_text="Please cancel my subscription.",
        metadata={"ticket_id": "T-22", "account_id": "A-103"},
        intent="subscription_cancellation",
    )

    updated = fetch_context(state)

    assert "account_result" in updated.context
    assert "get_account_status" in updated.used_tools


def test_classify_and_draft_use_structured_llm(monkeypatch) -> None:
    """Populate workflow state from structured LLM calls."""
    fake_llm = FakeLLM(
        [
            ClassificationResult(intent="order_status", rationale="tracking request"),
            DraftResponseResult(response="Your order is on the way."),
        ]
    )
    monkeypatch.setattr("src.workflow.build_langchain_llm", lambda temperature=0: fake_llm)

    state = WorkflowState(
        ticket_text="Where is order #2345?",
        metadata={"ticket_id": "T-3", "order_id": "2345"},
    )
    classified = classify_intent(state)
    classified.route = "logistics"
    classified.should_escalate = False
    drafted = draft_response(classified)

    assert drafted.intent == "order_status"
    assert drafted.response == "Your order is on the way."


def test_draft_response_requires_complete_state() -> None:
    """Reject drafting when the workflow state is incomplete."""
    state = WorkflowState(
        ticket_text="Where is my order?",
        metadata={"ticket_id": "T-30"},
        intent="order_status",
    )

    with pytest.raises(ValueError):
        draft_response(state)


def test_run_workflow_composes_all_steps(monkeypatch) -> None:
    """Run the end-to-end workflow with mocked model boundaries."""
    fake_llm = FakeLLM(
        [
            ClassificationResult(intent="billing_issue", rationale="duplicate charge"),
            DraftResponseResult(response="I am escalating this billing issue to finance."),
        ]
    )
    monkeypatch.setattr("src.workflow.build_langchain_llm", lambda temperature=0: fake_llm)

    result = run_workflow(
        "I was charged twice for my order #1234.",
        {"ticket_id": "T-4", "order_id": "1234"},
    )

    assert result.intent == "billing_issue"
    assert result.route == "escalations"
    assert result.tool_call_count >= 2
