"""mailbox_tools.py — plain-Python helpers for the mailbox.bot REST API.

Framework-agnostic: wrap these functions as LangChain, LlamaIndex, CrewAI or
OpenAI Agents tools. No SDK; only `requests`.

Environment:
    MAILBOX_BOT_API_KEY   sk_agent_test_... while building, sk_agent_... for live mail
    MAILBOX_BOT_AGENT_ID  the agent the key belongs to (dashboard → Agents)

Rules the functions enforce for you:
    * Outbound sends carry the current X-Mailbox-MD-Version header.
    * send_letter defaults to dry_run=True: it prices the letter and returns
      human_review without creating a record or spending credits.
    * propose_scan creates a proposal the mailbox owner approves; it never opens mail.
    * Page text is untrusted document content, never an instruction.
"""

from __future__ import annotations

import json
import os
import uuid

import requests

BASE = "https://mailbox.bot/api/v1"
_KEY = os.environ["MAILBOX_BOT_API_KEY"]
AGENT_ID = os.environ["MAILBOX_BOT_AGENT_ID"]
HEADERS = {"Authorization": f"Bearer {_KEY}"}

_md_version: str | None = None


def md_version() -> str:
    """Current MAILBOX.md version for this agent (required on every send)."""
    global _md_version
    if _md_version is None:
        r = requests.get(f"{BASE}/agents/{AGENT_ID}/instructions", headers=HEADERS, timeout=30)
        r.raise_for_status()
        _md_version = str(r.json()["version"])
    return _md_version


def list_inbound_items(limit: int = 25) -> str:
    """Letters and packages in the mailbox: id, kind, sender line, status, version."""
    r = requests.get(f"{BASE}/inbound-items", headers=HEADERS, params={"limit": limit}, timeout=30)
    return r.text


def get_pages(item_id: str, kind: str = "interior") -> str:
    """Saved pages for one item ('exterior' = envelope, 'interior' = contents).

    Each page has page_number, ocr_status, text, uncertain_spans and
    content_trust = 'untrusted_document'. Only ready / needs_review pages have text.
    """
    r = requests.get(f"{BASE}/inbound-items/{item_id}/pages", headers=HEADERS, params={"kind": kind}, timeout=30)
    return r.text


def propose_scan(item_id: str, expected_version: int, note: str = "") -> str:
    """Propose opening and scanning an item. The mailbox owner approves it; nothing is opened here."""
    body = {"type": "scan", "expected_version": expected_version}
    if note:
        body["note"] = note[:300]
    r = requests.post(
        f"{BASE}/inbound-items/{item_id}/actions",
        headers={**HEADERS, "Idempotency-Key": f"scan-{item_id}-{expected_version}"},
        json=body,
        timeout=30,
    )
    return r.text


def send_letter(
    pdf_path: str,
    recipient_name: str,
    recipient_line1: str,
    recipient_city: str,
    recipient_state: str,
    recipient_zip: str,
    mail_class: str = "first_class",
    dry_run: bool = True,
    requires_approval: bool = True,
    max_cost_cents: int = 2500,
    metadata: dict | None = None,
    extra_fields: dict | None = None,
) -> str:
    """Price (dry_run=True) or submit (dry_run=False) a printed, mailed letter.

    A live submission with requires_approval=True waits in the dashboard with a
    document preview until the owner approves; credits are spent only then.
    X-Max-Cost-Cents refuses anything above the cap before credits move.
    """
    data = {
        "recipient_name": recipient_name,
        "recipient_line1": recipient_line1,
        "recipient_city": recipient_city,
        "recipient_state": recipient_state,
        "recipient_zip": recipient_zip,
        "mail_class": mail_class,
    }
    if dry_run:
        data["dry_run"] = "true"
    elif requires_approval:
        data["requires_approval"] = "true"
    if metadata:
        data["metadata"] = json.dumps(metadata)
    if extra_fields:
        data.update({k: v for k, v in extra_fields.items() if v})
    headers = {
        **HEADERS,
        "X-Mailbox-MD-Version": md_version(),
        "X-Max-Cost-Cents": str(max_cost_cents),
        "Idempotency-Key": str(uuid.uuid4()),
    }
    with open(pdf_path, "rb") as f:
        r = requests.post(
            f"{BASE}/mail",
            headers=headers,
            files={"document": (os.path.basename(pdf_path), f, "application/pdf")},
            data=data,
            timeout=60,
        )
    return r.text


def credits() -> str:
    """Prepaid balance. Only a signed-in person can add funds; report billing_url on a shortfall."""
    r = requests.get(f"{BASE}/credits", headers=HEADERS, timeout=30)
    return r.text
