"""Assist / takeover session storage — Spec 010 Phase A."""

from __future__ import annotations

import hashlib
import json
import sqlite3
from datetime import datetime, timezone
from typing import Any

ASSIST_MIN_RANK = 2  # domain.validated
ASSIST_MIN_STAGE = "domain_validated"

TICKET_ASSIST_STATUSES = frozenset({"open", "escalated", "assisting", "resolved", "closed"})
TICKET_ACTIVE_STATUSES = frozenset({"open", "escalated", "assisting", "resolved"})


def init_assist_schema(conn: sqlite3.Connection) -> None:
    conn.executescript(
        """
        CREATE TABLE IF NOT EXISTS assist_sessions (
            id INTEGER PRIMARY KEY,
            session_id TEXT NOT NULL,
            ticket_id INTEGER,
            initiated_by TEXT NOT NULL,
            initiated_by_user TEXT,
            status TEXT NOT NULL,
            funnel_stage TEXT,
            domain TEXT,
            takeover_token_hash TEXT,
            started_at TEXT NOT NULL,
            ended_at TEXT,
            audit_summary TEXT
        );
        CREATE INDEX IF NOT EXISTS idx_assist_sessions_session
            ON assist_sessions(session_id);
        CREATE TABLE IF NOT EXISTS assist_actions (
            id INTEGER PRIMARY KEY,
            assist_session_id INTEGER NOT NULL,
            actor TEXT NOT NULL,
            action TEXT NOT NULL,
            payload TEXT,
            created_at TEXT NOT NULL
        );
        """
    )
    cols = {row[1] for row in conn.execute("PRAGMA table_info(tickets)").fetchall()}
    for col, ddl in (
        ("session_id", "TEXT"),
        ("assist_mode", "TEXT"),
        ("assisted_by", "TEXT"),
        ("assisted_at", "TEXT"),
        ("client_paused", "INTEGER NOT NULL DEFAULT 0"),
    ):
        if col not in cols:
            conn.execute(f"ALTER TABLE tickets ADD COLUMN {col} {ddl}")


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


def _parse_payload(raw: str | None) -> dict:
    if not raw:
        return {}
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        return {}


def log_action(
    conn: sqlite3.Connection,
    assist_session_id: int,
    actor: str,
    action: str,
    payload: dict | None = None,
) -> None:
    conn.execute(
        """
        INSERT INTO assist_actions (assist_session_id, actor, action, payload, created_at)
        VALUES (?, ?, ?, ?, ?)
        """,
        (assist_session_id, actor, action, json.dumps(payload or {}), _now()),
    )


def get_active_assist(conn: sqlite3.Connection, session_id: str) -> dict | None:
    sid = (session_id or "").strip()
    if not sid:
        return None
    row = conn.execute(
        """
        SELECT * FROM assist_sessions
        WHERE session_id = ? AND status = 'active' AND ended_at IS NULL
        ORDER BY id DESC LIMIT 1
        """,
        (sid,),
    ).fetchone()
    return dict(row) if row else None


def get_open_assist(conn: sqlite3.Connection, session_id: str) -> dict | None:
    """Sessão de assistência aberta (escalated ou active)."""
    sid = (session_id or "").strip()
    if not sid:
        return None
    row = conn.execute(
        """
        SELECT * FROM assist_sessions
        WHERE session_id = ? AND ended_at IS NULL AND status IN ('escalated', 'active')
        ORDER BY id DESC LIMIT 1
        """,
        (sid,),
    ).fetchone()
    return dict(row) if row else None


def get_assist_state_map(conn: sqlite3.Connection, session_ids: list[str]) -> dict[str, dict]:
    if not session_ids:
        return {}
    placeholders = ",".join("?" * len(session_ids))
    rows = conn.execute(
        f"""
        SELECT a.* FROM assist_sessions a
        INNER JOIN (
            SELECT session_id, MAX(id) AS max_id
            FROM assist_sessions
            WHERE session_id IN ({placeholders}) AND ended_at IS NULL
            GROUP BY session_id
        ) latest ON a.id = latest.max_id
        """,
        session_ids,
    ).fetchall()
    out: dict[str, dict] = {}
    for row in rows:
        item = dict(row)
        out[item["session_id"]] = item
    return out


def session_funnel_meta(
    conn: sqlite3.Connection,
    session_id: str,
    funnel_event_rank: dict[str, int],
    funnel_stage_by_rank: dict[int, str],
    onboard_source: str = "vm112-onboard",
) -> dict[str, Any]:
    sid = (session_id or "").strip()
    rows = conn.execute(
        """
        SELECT event_type, payload, created_at
        FROM webhook_events
        WHERE source = ?
        ORDER BY id ASC
        """,
        (onboard_source,),
    ).fetchall()
    max_rank = 0
    domain = None
    failed = False
    last_event_at = None
    for row in rows:
        payload = _parse_payload(row["payload"])
        if (payload.get("session_id") or "").strip() != sid:
            continue
        if payload.get("domain"):
            domain = payload.get("domain")
        last_event_at = row["created_at"]
        if row["event_type"] == "onboarding.failed":
            failed = True
            max_rank = max(max_rank, 99)
        else:
            rank = funnel_event_rank.get(row["event_type"], 0)
            if not failed:
                max_rank = max(max_rank, rank)
    if failed:
        stage = "failed"
    else:
        stage = funnel_stage_by_rank.get(max_rank, "started")
    return {
        "session_id": sid,
        "domain": domain,
        "funnel_stage": stage,
        "funnel_rank": max_rank,
        "can_escalate": max_rank >= ASSIST_MIN_RANK or failed,
        "last_event_at": last_event_at,
    }


def find_ticket_by_session(conn: sqlite3.Connection, session_id: str) -> sqlite3.Row | None:
    sid = (session_id or "").strip()
    if not sid:
        return None
    row = conn.execute(
        "SELECT id, status, assigned_to, session_id, payload FROM tickets WHERE session_id = ? ORDER BY id DESC LIMIT 1",
        (sid,),
    ).fetchone()
    if row:
        return row
    rows = conn.execute("SELECT id, status, assigned_to, session_id, payload FROM tickets ORDER BY id DESC LIMIT 300").fetchall()
    for item in rows:
        payload = _parse_payload(item["payload"])
        if (payload.get("session_id") or "").strip() == sid:
            return item
    return None


def ensure_onboard_ticket(
    conn: sqlite3.Connection,
    session_id: str,
    domain: str | None,
    tenant_id: int = 1,
) -> int:
    existing = find_ticket_by_session(conn, session_id)
    if existing:
        return int(existing["id"])
    now = _now()
    payload = json.dumps({
        "event": "onboarding.escalated",
        "domain": domain,
        "session_id": session_id,
        "source": "vm112-onboard",
        "data": {"reason": "desk_assist"},
    })
    subject = f"[assist] {domain or 'sem dominio'} — {session_id[:12]}"
    cur = conn.execute(
        """
        INSERT INTO tickets
        (tenant_id, subject, status, payload, created_at, session_id, assigned_to, assigned_at)
        VALUES (?, ?, 'escalated', ?, ?, ?, NULL, NULL)
        """,
        (tenant_id, subject, payload, now, session_id),
    )
    return int(cur.lastrowid)


def hash_token(token: str) -> str:
    return hashlib.sha256(token.encode()).hexdigest()
