"""support_desk.py: triage, route, and escalate support tickets under an SLA.

The "support" in operations support: when users and systems file tickets, something has to read each
one, decide how urgent it is, send it to the right tier, and make sure it is answered in time, or
escalate it before an SLA is missed. Here triage is a deterministic keyword classifier so the lab runs
offline and reproducibly; in production you would swap in an LLM classifier (M5/M9) behind the same
`triage()` function. Everything else, the routing, the SLA clock, the escalation, is the same.

No dependencies, no network, deterministic.
"""
from dataclasses import dataclass, field

# severity -> (tier that owns the first response, SLA minutes to first response)
ROUTING = {
    "sev1": ("L2", 15),
    "sev2": ("L2", 60),
    "sev3": ("L1", 240),
}

# Keyword signals -> severity. A stand-in for an LLM classifier; deterministic on purpose.
RULES = [
    ("sev1", ["outage", "is down", "cannot log in", "data loss", "breach", "leaked", "all users", "503"]),
    ("sev2", ["error", "failing", "slow", "timeout", "degraded", "some users", "wrong answer"]),
    ("sev3", ["question", "how do i", "feature request", "typo", "cosmetic", "documentation"]),
]

LOW_CONFIDENCE = 0.5            # below this, do not trust the label, send to a human
ESCALATE_LADDER = {"L1": "L2", "L2": "L3", "L3": "L3"}


@dataclass
class Ticket:
    id: str
    text: str
    severity: str = ""
    confidence: float = 0.0
    tier: str = ""
    sla_minutes: int = 0
    status: str = "new"        # new -> routed -> escalated -> resolved, or -> human_review
    notes: list = field(default_factory=list)


def triage(ticket: Ticket, rules=RULES) -> Ticket:
    """Classify severity and a confidence score from the ticket text."""
    text = ticket.text.lower()
    best_sev, best_hits = "sev3", 0
    for sev, keywords in rules:
        hits = sum(1 for k in keywords if k in text)
        if hits > best_hits:
            best_sev, best_hits = sev, hits
    # More/stronger keyword hits -> more confident. No hits at all -> very unsure.
    ticket.confidence = round(min(1.0, 0.4 + 0.3 * best_hits), 2) if best_hits else 0.2
    ticket.severity = best_sev
    return ticket


def route(ticket: Ticket, routing=ROUTING, low_confidence=LOW_CONFIDENCE) -> Ticket:
    """Assign a tier and SLA. If triage was unsure, send to a human instead of trusting the label."""
    if ticket.confidence < low_confidence:
        ticket.status, ticket.tier = "human_review", "human"
        ticket.notes.append(f"low confidence {ticket.confidence} -> human triage (do not auto-route)")
        return ticket
    tier, sla = routing[ticket.severity]
    ticket.tier, ticket.sla_minutes, ticket.status = tier, sla, "routed"
    ticket.notes.append(f"routed {ticket.severity} -> {tier} (SLA {sla}m)")
    return ticket


def sla_check(ticket: Ticket, minutes_waited: int, ladder=ESCALATE_LADDER) -> Ticket:
    """If the first response missed its SLA, escalate one tier up the ladder."""
    if ticket.status != "routed":
        return ticket
    if minutes_waited > ticket.sla_minutes:
        ticket.tier = ladder[ticket.tier]
        ticket.status = "escalated"
        ticket.notes.append(
            f"SLA breach ({minutes_waited}m > {ticket.sla_minutes}m) -> escalate to {ticket.tier}")
    return ticket


def handle(ticket: Ticket, minutes_waited: int) -> Ticket:
    """The full desk path for one ticket: triage -> route -> SLA check."""
    triage(ticket)
    route(ticket)
    sla_check(ticket, minutes_waited)
    return ticket
