"""Desk governance — audit log, user meta, module access. Spec 040 · DS-API-001."""

from __future__ import annotations

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

GOVERNANCE_MODULES = (
    "desk",
    "openpanel",
    "billing",
    "api",
    "security",
    "ai_agents",
)

ACCESS_LEVELS = ("none", "read", "partial", "full")

ROLE_GROUPS: dict[str, str] = {
    "super_admin": "Ops",
    "ops_lead": "Ops",
    "technician": "Ops",
    "noc": "Ops",
    "sales_admin": "Comercial",
    "sales_support": "Comercial",
    "finance": "Negócio",
    "marketing": "Negócio",
    "seo": "Negócio",
    "developer": "Plataforma",
    "devops": "Plataforma",
    "security_analyst": "Plataforma",
    "content_editor": "Plataforma",
    "agentic_operator": "Plataforma",
    "partner": "Externo",
}


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


def init_governance_schema(conn: sqlite3.Connection) -> None:
    conn.executescript(
        """
        CREATE TABLE IF NOT EXISTS desk_governance_audit (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            actor_username TEXT NOT NULL,
            actor_role TEXT,
            action TEXT NOT NULL,
            target_type TEXT NOT NULL,
            target_id TEXT NOT NULL,
            summary TEXT NOT NULL,
            payload_json TEXT,
            created_at TEXT NOT NULL
        );
        CREATE INDEX IF NOT EXISTS idx_desk_gov_audit_target
            ON desk_governance_audit(target_type, target_id);
        CREATE INDEX IF NOT EXISTS idx_desk_gov_audit_created
            ON desk_governance_audit(created_at DESC);

        CREATE TABLE IF NOT EXISTS desk_user_meta (
            username TEXT PRIMARY KEY,
            internal_id TEXT NOT NULL UNIQUE,
            main_group TEXT,
            secondary_groups_json TEXT NOT NULL DEFAULT '[]',
            account_status TEXT NOT NULL DEFAULT 'active',
            force_password_change INTEGER NOT NULL DEFAULT 0,
            api_access INTEGER NOT NULL DEFAULT 0,
            notifications_enabled INTEGER NOT NULL DEFAULT 1,
            module_permissions_json TEXT NOT NULL DEFAULT '{}',
            invite_token TEXT,
            notes TEXT,
            created_at TEXT NOT NULL,
            updated_at TEXT NOT NULL
        );
        """
    )


def _gen_internal_id() -> str:
    return f"LB-{secrets.token_hex(4).upper()}"


def _gen_invite_token() -> str:
    return secrets.token_urlsafe(24)


def group_for_role(role: str) -> str:
    return ROLE_GROUPS.get(role, "—")


def default_module_permissions(role: str) -> dict[str, str]:
    base = {m: "none" for m in GOVERNANCE_MODULES}
    if role == "super_admin":
        return {m: "full" for m in GOVERNANCE_MODULES}
    if role in ("ops_lead", "devops"):
        base.update({"desk": "full", "openpanel": "partial", "security": "partial", "api": "read"})
    elif role == "technician":
        base.update({"desk": "partial", "openpanel": "read"})
    elif role == "finance":
        base.update({"billing": "full", "desk": "read"})
    elif role == "agentic_operator":
        base.update({"ai_agents": "full", "desk": "partial"})
    return base


def log_audit(
    conn: sqlite3.Connection,
    *,
    actor_username: str,
    actor_role: str | None,
    action: str,
    target_type: str,
    target_id: str,
    summary: str,
    payload: dict | None = None,
) -> dict:
    now = _now()
    conn.execute(
        """
        INSERT INTO desk_governance_audit
        (actor_username, actor_role, action, target_type, target_id, summary, payload_json, created_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        """,
        (
            actor_username,
            actor_role,
            action,
            target_type,
            target_id,
            summary,
            json.dumps(payload or {}, ensure_ascii=False),
            now,
        ),
    )
    return {
        "actor_username": actor_username,
        "actor_role": actor_role,
        "action": action,
        "target_type": target_type,
        "target_id": target_id,
        "summary": summary,
        "created_at": now,
    }


def list_audit(
    conn: sqlite3.Connection,
    *,
    target_type: str | None = None,
    target_id: str | None = None,
    limit: int = 50,
) -> list[dict]:
    q = "SELECT * FROM desk_governance_audit WHERE 1=1"
    params: list[Any] = []
    if target_type:
        q += " AND target_type = ?"
        params.append(target_type)
    if target_id:
        q += " AND target_id = ?"
        params.append(target_id)
    q += " ORDER BY id DESC LIMIT ?"
    params.append(limit)
    rows = conn.execute(q, params).fetchall()
    out = []
    for row in rows:
        item = dict(row)
        try:
            item["payload"] = json.loads(item.pop("payload_json") or "{}")
        except json.JSONDecodeError:
            item["payload"] = {}
        out.append(item)
    return out


def get_user_meta(conn: sqlite3.Connection, username: str) -> dict | None:
    row = conn.execute(
        "SELECT * FROM desk_user_meta WHERE username = ?",
        (username,),
    ).fetchone()
    if not row:
        return None
    item = dict(row)
    try:
        item["secondary_groups"] = json.loads(item.pop("secondary_groups_json") or "[]")
    except json.JSONDecodeError:
        item["secondary_groups"] = []
    try:
        item["module_permissions"] = json.loads(item.pop("module_permissions_json") or "{}")
    except json.JSONDecodeError:
        item["module_permissions"] = {}
    item["force_password_change"] = bool(item.get("force_password_change"))
    item["api_access"] = bool(item.get("api_access"))
    item["notifications_enabled"] = bool(item.get("notifications_enabled"))
    return item


def ensure_user_meta(conn: sqlite3.Connection, username: str, role: str) -> dict:
    existing = get_user_meta(conn, username)
    if existing:
        return existing
    now = _now()
    perms = default_module_permissions(role)
    conn.execute(
        """
        INSERT INTO desk_user_meta
        (username, internal_id, main_group, secondary_groups_json, account_status,
         force_password_change, api_access, notifications_enabled, module_permissions_json,
         invite_token, notes, created_at, updated_at)
        VALUES (?, ?, ?, '[]', 'active', 0, 0, 1, ?, NULL, NULL, ?, ?)
        """,
        (
            username,
            _gen_internal_id(),
            group_for_role(role),
            json.dumps(perms),
            now,
            now,
        ),
    )
    return get_user_meta(conn, username) or {}


def upsert_user_meta(conn: sqlite3.Connection, username: str, **fields: Any) -> dict:
    row = get_user_meta(conn, username)
    now = _now()
    if not row:
        raise ValueError("user meta missing")
    secondary = fields.get("secondary_groups", row.get("secondary_groups", []))
    perms = fields.get("module_permissions", row.get("module_permissions", {}))
    conn.execute(
        """
        UPDATE desk_user_meta SET
            main_group = ?,
            secondary_groups_json = ?,
            account_status = ?,
            force_password_change = ?,
            api_access = ?,
            notifications_enabled = ?,
            module_permissions_json = ?,
            invite_token = ?,
            notes = ?,
            updated_at = ?
        WHERE username = ?
        """,
        (
            fields.get("main_group", row.get("main_group")),
            json.dumps(secondary),
            fields.get("account_status", row.get("account_status", "active")),
            1 if fields.get("force_password_change", row.get("force_password_change")) else 0,
            1 if fields.get("api_access", row.get("api_access")) else 0,
            1 if fields.get("notifications_enabled", row.get("notifications_enabled", True)) else 0,
            json.dumps(perms),
            fields.get("invite_token", row.get("invite_token")),
            fields.get("notes", row.get("notes")),
            now,
            username,
        ),
    )
    return get_user_meta(conn, username) or {}
