"""Deterministic support-ticket workflow used for the fixed-path demo."""

from __future__ import annotations

from pathlib import Path
from typing import Any

from langsmith import traceable

from src.config import build_langchain_llm
from src.schemas import ClassificationResult, DraftResponseResult, SystemOutput, WorkflowState
from src.tools import get_account_status, get_order_status, get_policy, search_kb

PROMPTS_DIR = Path(__file__).resolve().parent.parent / "prompts"

INTENT_TO_ROUTE = {
    "refund_request": "general",
    "order_status": "logistics",
    "billing_issue": "finance",
    "account_access": "account_support",
    "damaged_item": "general",
    "subscription_cancellation": "retention",
    "unknown": "general",
}

INTENT_TO_POLICY = {
    "refund_request": "refund_policy",
    "billing_issue": "billing_dispute_policy",
    "account_access": "account_recovery_policy",
    "damaged_item": "damaged_item_policy",
    "subscription_cancellation": "cancellation_policy",
}


def _read_prompt(name: str) -> str:
    """Read a prompt file from the prompts directory."""
    return (PROMPTS_DIR / name).read_text()


@traceable(name="workflow.classify_intent", run_type="chain")
def classify_intent(state: WorkflowState) -> WorkflowState:
    """Classify the ticket intent with a structured LLM call."""
    llm = build_langchain_llm(temperature=0)
    structured_llm = llm.with_structured_output(ClassificationResult)
    result = structured_llm.invoke(
        [
            ("system", _read_prompt("workflow_classifier_prompt.txt")),
            (
                "human",
                f"Ticket metadata: {state.metadata.model_dump()}\n"
                f"Ticket text: {state.ticket_text}",
            ),
        ]
    )
    state.intent = result.intent
    return state


@traceable(name="workflow.fetch_context", run_type="chain")
def fetch_context(state: WorkflowState) -> WorkflowState:
    """Fetch deterministic context based on the current workflow state."""
    if state.intent is None:
        raise ValueError("Intent must be set before fetching context.")

    context: dict[str, Any] = {}

    kb_result = search_kb(state.ticket_text)
    state.used_tools.append("search_kb")
    context["kb_result"] = kb_result["payload"]

    policy_name = INTENT_TO_POLICY.get(state.intent)
    if policy_name:
        policy_result = get_policy(policy_name)
        state.used_tools.append("get_policy")
        context["policy_result"] = policy_result["payload"]

    if state.metadata.order_id and state.intent in {
        "order_status",
        "billing_issue",
        "refund_request",
        "damaged_item",
    }:
        order_result = get_order_status(state.metadata.order_id)
        state.used_tools.append("get_order_status")
        context["order_result"] = order_result["payload"]

    if state.metadata.account_id and state.intent in {
        "account_access",
        "subscription_cancellation",
    }:
        account_result = get_account_status(state.metadata.account_id)
        state.used_tools.append("get_account_status")
        context["account_result"] = account_result["payload"]

    state.context = context
    return state


def _contains_any(text: str, keywords: list[str]) -> bool:
    """Check whether the text contains any of the provided keywords."""
    text_lc = text.lower()
    return any(keyword in text_lc for keyword in keywords)


@traceable(name="workflow.determine_escalation", run_type="chain")
def determine_escalation(ticket_text: str, intent: str, metadata: dict[str, Any]) -> bool:
    """Decide whether a ticket should be escalated using explicit rules."""
    text = ticket_text.lower()
    should_escalate = False

    if intent == "billing_issue" and _contains_any(
        text,
        ["charged twice", "double charged", "duplicate charge"],
    ):
        should_escalate = True
    elif intent == "damaged_item" and _contains_any(
        text,
        ["unsafe", "hazard", "broken glass", "second time"],
    ):
        should_escalate = True
    elif intent == "account_access" and _contains_any(
        text,
        ["locked", "suspicious", "hacked", "cannot verify"],
    ):
        should_escalate = True
    elif intent == "refund_request" and _contains_any(
        text,
        ["outside", "special exception", "manager"],
    ):
        should_escalate = True

    if intent == "billing_issue" and not metadata.get("order_id"):
        should_escalate = True
    if intent == "account_access" and not metadata.get("account_id"):
        should_escalate = True

    return should_escalate


@traceable(name="workflow.decide_route_and_escalation", run_type="chain")
def decide_route_and_escalation(state: WorkflowState) -> WorkflowState:
    """Assign the route and escalation flag from the workflow state."""
    if state.intent is None:
        raise ValueError("Intent must be set before routing.")

    state.route = INTENT_TO_ROUTE[state.intent]
    should_escalate = determine_escalation(
        state.ticket_text,
        state.intent,
        state.metadata.model_dump(),
    )

    if should_escalate:
        state.route = "escalations"

    state.should_escalate = should_escalate
    return state


@traceable(name="workflow.draft_response", run_type="chain")
def draft_response(state: WorkflowState) -> WorkflowState:
    """Draft the final customer response from the workflow state."""
    if state.intent is None or state.route is None or state.should_escalate is None:
        raise ValueError("Workflow state is incomplete before drafting a response.")

    llm = build_langchain_llm(temperature=0)
    structured_llm = llm.with_structured_output(DraftResponseResult)
    result = structured_llm.invoke(
        [
            ("system", _read_prompt("workflow_response_prompt.txt")),
            (
                "human",
                "Prepare the final response using the workflow state below.\n"
                f"Ticket text: {state.ticket_text}\n"
                f"Metadata: {state.metadata.model_dump()}\n"
                f"Intent: {state.intent}\n"
                f"Route: {state.route}\n"
                f"Should escalate: {state.should_escalate}\n"
                f"Context: {state.context}\n",
            ),
        ]
    )
    state.response = result.response
    return state


@traceable(name="workflow.run_workflow", run_type="chain")
def run_workflow(ticket_text: str, metadata: dict[str, Any]) -> SystemOutput:
    """Run the full fixed workflow and return a structured system output."""
    state = WorkflowState(ticket_text=ticket_text, metadata=metadata)
    for step in (classify_intent, fetch_context, decide_route_and_escalation, draft_response):
        state = step(state)

    return SystemOutput(
        ticket_id=state.metadata.ticket_id,
        intent=state.intent,
        route=state.route,
        should_escalate=state.should_escalate,
        response=state.response,
        used_tools=state.used_tools,
        tool_call_count=len(state.used_tools),
    )
