"""Bloqueios ACCOUNT_EXISTS — libertar e-mail Carbonio (Spec 022)."""

from __future__ import annotations

import json
import re
from datetime import datetime, timezone
from typing import Any

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")


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


def init_schema(conn) -> None:
    conn.execute(
        """
        CREATE TABLE IF NOT EXISTS carbonio_account_blocks (
            id INTEGER PRIMARY KEY,
            email TEXT NOT NULL,
            domain TEXT NOT NULL,
            session_id TEXT,
            ticket_id INTEGER,
            webhook_event_id INTEGER,
            error_message TEXT,
            status TEXT NOT NULL DEFAULT 'pending',
            resolved_by TEXT,
            resolved_at TEXT,
            resolution_note TEXT,
            created_at TEXT NOT NULL
        )
        """
    )
    conn.execute(
        """
        CREATE INDEX IF NOT EXISTS idx_carbonio_blocks_status
        ON carbonio_account_blocks(status, created_at DESC)
        """
    )
    conn.execute(
        """
        CREATE INDEX IF NOT EXISTS idx_carbonio_blocks_session
        ON carbonio_account_blocks(session_id)
        """
    )


def is_account_exists_failure(event: str, data: dict | None) -> bool:
    if event != "onboarding.failed":
        return False
    if not isinstance(data, dict):
        return False
    err = str(data.get("error") or data.get("message") or data.get("reason") or "")
    low = err.lower()
    return (
        "account_exists" in low
        or "account.exists" in low
        or "já existe" in low
        or "already exists" in low
    )


def _extract_email(domain: str | None, data: dict | None) -> str | None:
    if isinstance(data, dict):
        for key in ("email", "account", "mailbox"):
            val = (data.get(key) or "").strip().lower()
            if EMAIL_RE.match(val):
                return val
        err = str(data.get("error") or "")
        m = re.search(r"[\w.+-]+@[\w.-]+\.\w+", err)
        if m:
            return m.group(0).lower()
    if domain and isinstance(data, dict):
        local = (data.get("local_part") or "").strip().lower()
        if local:
            return f"{local}@{domain.lower().strip()}"
    return None


def upsert_from_webhook(
    conn,
    *,
    event: str,
    domain: str | None,
    session_id: str | None,
    data: dict | None,
    webhook_event_id: int | None,
    ticket_id: int | None,
) -> dict[str, Any] | None:
    if not is_account_exists_failure(event, data):
        return None
    email = _extract_email(domain, data)
    if not email:
        return None
    dom = (domain or email.split("@", 1)[-1]).lower().strip()
    sid = (session_id or "").strip() or None
    err_msg = str((data or {}).get("error") or "")[:2000]
    now = _now()

    if sid:
        row = conn.execute(
            """
            SELECT id, status FROM carbonio_account_blocks
            WHERE email = ? AND session_id = ? AND status = 'pending'
            ORDER BY id DESC LIMIT 1
            """,
            (email, sid),
        ).fetchone()
        if row:
            conn.execute(
                """
                UPDATE carbonio_account_blocks
                SET error_message = ?, webhook_event_id = COALESCE(?, webhook_event_id),
                    ticket_id = COALESCE(?, ticket_id)
                WHERE id = ?
                """,
                (err_msg, webhook_event_id, ticket_id, row["id"]),
            )
            conn.commit()
            return get_block(conn, int(row["id"]))

    cur = conn.execute(
        """
        INSERT INTO carbonio_account_blocks
        (email, domain, session_id, ticket_id, webhook_event_id, error_message, status, created_at)
        VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)
        """,
        (email, dom, sid, ticket_id, webhook_event_id, err_msg, now),
    )
    conn.commit()
    return get_block(conn, int(cur.lastrowid))


def get_block(conn, block_id: int) -> dict[str, Any] | None:
    row = conn.execute(
        "SELECT * FROM carbonio_account_blocks WHERE id = ?",
        (block_id,),
    ).fetchone()
    return _row_to_dict(row) if row else None


def _row_to_dict(row) -> dict[str, Any]:
    return {
        "id": row["id"],
        "email": row["email"],
        "domain": row["domain"],
        "session_id": row["session_id"],
        "ticket_id": row["ticket_id"],
        "webhook_event_id": row["webhook_event_id"],
        "error_message": row["error_message"],
        "status": row["status"],
        "resolved_by": row["resolved_by"],
        "resolved_at": row["resolved_at"],
        "resolution_note": row["resolution_note"],
        "created_at": row["created_at"],
    }


def list_blocks(
    conn,
    *,
    status: str | None = "pending",
    session_id: str | None = None,
    ticket_id: int | None = None,
    limit: int = 100,
) -> dict[str, Any]:
    limit = max(1, min(limit, 500))
    clauses = []
    params: list[Any] = []
    if status and status != "all":
        clauses.append("status = ?")
        params.append(status)
    if session_id:
        clauses.append("session_id = ?")
        params.append(session_id.strip())
    if ticket_id:
        clauses.append("ticket_id = ?")
        params.append(ticket_id)
    where = f"WHERE {' AND '.join(clauses)}" if clauses else ""
    rows = conn.execute(
        f"""
        SELECT * FROM carbonio_account_blocks {where}
        ORDER BY id DESC LIMIT ?
        """,
        (*params, limit),
    ).fetchall()
    total = conn.execute(
        f"SELECT COUNT(*) FROM carbonio_account_blocks {where}",
        tuple(params),
    ).fetchone()[0]
    return {
        "blocks": [_row_to_dict(r) for r in rows],
        "total": total,
    }


def resolve_block(
    conn,
    block_id: int,
    *,
    resolved_by: str,
    note: str = "",
) -> dict[str, Any]:
    now = _now()
    cur = conn.execute(
        """
        UPDATE carbonio_account_blocks
        SET status = 'resolved', resolved_by = ?, resolved_at = ?, resolution_note = ?
        WHERE id = ? AND status = 'pending'
        """,
        (resolved_by, now, note[:500], block_id),
    )
    if cur.rowcount == 0:
        existing = get_block(conn, block_id)
        if existing and existing["status"] == "resolved":
            raise ValueError("already_resolved")
        raise ValueError("not_found_or_locked")
    conn.commit()
    return get_block(conn, block_id) or {}


def append_ticket_resolution_note(conn, ticket_id: int, *, email: str, by: str) -> None:
    row = conn.execute("SELECT payload FROM tickets WHERE id = ?", (ticket_id,)).fetchone()
    if not row:
        return
    try:
        payload = json.loads(row["payload"] or "{}")
    except json.JSONDecodeError:
        payload = {}
    notes = payload.get("carbonio_release_notes") or []
    notes.append({"at": _now(), "email": email, "by": by, "action": "account_deleted"})
    payload["carbonio_release_notes"] = notes[-20:]
    conn.execute(
        "UPDATE tickets SET payload = ? WHERE id = ?",
        (json.dumps(payload, ensure_ascii=False), ticket_id),
    )
    conn.commit()
