"""
Tools the agent is allowed to use.

Each tool is an ordinary Python function. There is no agent framework,
no LLM, and no network call. The agent script chooses among these with
if/elif. That fixed list is the whole toolbox.
"""

from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path

import joblib

HERE = Path(__file__).resolve().parent
MODEL_PATH = HERE / "model" / "inbox_model.joblib"
POLICIES_PATH = HERE / "data" / "policies.json"
LOG_PATH = HERE / "logs" / "agent_log.jsonl"

KNOWN_LABELS = (
    "password_reset",
    "billing",
    "shipping",
    "spam",
    "unknown",
)

# Used if policies.json is missing. Same text generate_data.py writes.
FALLBACK_POLICIES = {
    "password_reset": (
        "Send the password-reset steps. Never ask for the current password. "
        "Offer to unlock the account after the customer confirms their email."
    ),
    "billing": (
        "Explain the charge using the invoice id if we have one. "
        "Refunds need a supervisor review — do not promise a refund in the draft."
    ),
    "shipping": (
        "Share tracking help and the delivery window. "
        "Address changes are allowed only before the label is printed."
    ),
    "spam": (
        "Do not reply. Mark the thread as spam and stop. "
        "Never click links from these messages."
    ),
    "unknown": (
        "The predicted label is not one we handle. Log the message and stop "
        "tools for this item. A human should read it."
    ),
}

REPLY_TEMPLATES = {
    "password_reset": (
        "Hi {sender},\n\n"
        "We can help you get back into your account. Use the password-reset "
        "link from the login page, then try signing in again. We will never "
        "ask you to send your current password by email.\n\n"
        "— Support"
    ),
    "billing": (
        "Hi {sender},\n\n"
        "Thanks for writing about billing. We will review the invoice and "
        "the charge on file. If a refund is appropriate, a supervisor has "
        "to approve it — this note is not an approval.\n\n"
        "— Support"
    ),
    "shipping": (
        "Hi {sender},\n\n"
        "We are looking up your shipment and tracking details. If you need "
        "the delivery address changed, tell us before the label is printed.\n\n"
        "— Support"
    ),
}

_model = None


def message_text(message: dict) -> str:
    return f"{message.get('subject', '')} {message.get('body', '')}".strip()


def classify(text: str, model_path: Path = MODEL_PATH) -> str:
    """Return a predicted label for one string. Loads the saved sklearn pipeline."""
    global _model
    if _model is None:
        if not model_path.exists():
            raise FileNotFoundError(
                f"Missing {model_path}. Run: python generate_data.py then python train_model.py"
            )
        _model = joblib.load(model_path)
    label = str(_model.predict([text])[0])
    if label not in KNOWN_LABELS:
        return "unknown"
    return label


def reset_loaded_model() -> None:
    """Tests call this so a newly trained file is loaded on the next classify()."""
    global _model
    _model = None


def lookup_policy(label: str, policies_path: Path = POLICIES_PATH) -> str:
    """Read a local JSON file (or a dict fallback). No HTTP."""
    policies = dict(FALLBACK_POLICIES)
    if policies_path.exists():
        loaded = json.loads(policies_path.read_text(encoding="utf-8"))
        if isinstance(loaded, dict):
            policies.update(loaded)
    return policies.get(label, policies["unknown"])


def draft_reply(label: str, message: dict) -> str | None:
    """Fill a canned template. Spam and unknown get no customer-facing draft."""
    template = REPLY_TEMPLATES.get(label)
    if template is None:
        return None
    sender = message.get("from", "there")
    if "@" in sender:
        sender = sender.split("@", 1)[0]
    return template.format(sender=sender)


def write_log(
    record: dict,
    log_path: Path = LOG_PATH,
) -> Path:
    """Append one JSON object as a line in a log file (jsonl)."""
    log_path.parent.mkdir(parents=True, exist_ok=True)
    line = json.dumps(record, ensure_ascii=True) + "\n"
    with log_path.open("a", encoding="utf-8") as handle:
        handle.write(line)
    return log_path


def make_log_record(
    message: dict,
    label: str,
    policy: str,
    reply: str | None,
    tools_used: list[str],
) -> dict:
    return {
        "at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
        "id": message.get("id"),
        "subject": message.get("subject"),
        "label": label,
        "tools_used": tools_used,
        "policy": policy,
        "reply": reply,
    }
