"""Governance API — user wizard, audit. Spec 040 · DS-API-002."""

from __future__ import annotations

import json
import secrets
import string
from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field

from app import auth, mail_notify
from app.desk_governance_store import (
    ACCESS_LEVELS,
    GOVERNANCE_MODULES,
    ROLE_GROUPS,
    default_module_permissions,
    ensure_user_meta,
    get_user_meta,
    group_for_role,
    init_governance_schema,
    list_audit,
    log_audit,
    upsert_user_meta,
)
from app.permissions import ROLES, can_manage_users

router = APIRouter(prefix="/api/v1/governance", tags=["governance"])


class ModulePermissions(BaseModel):
    desk: str = "none"
    openpanel: str = "none"
    billing: str = "none"
    api: str = "none"
    security: str = "none"
    ai_agents: str = "none"


class UserWizardRequest(BaseModel):
    display_name: str = Field(min_length=1, max_length=120)
    email: str = Field(min_length=3, max_length=320)
    phone: str | None = Field(default=None, max_length=32)
    password: str = Field(min_length=6)
    account_status: str = Field(default="active", pattern="^(active|frozen|pending|invited)$")
    force_password_change: bool = False
    mfa_required: bool = False
    notifications_enabled: bool = True
    api_access: bool = False
    role: str
    main_group: str | None = None
    secondary_groups: list[str] = Field(default_factory=list)
    module_permissions: ModulePermissions | None = None
    notes: str | None = Field(default=None, max_length=200)
    send_invite_email: bool = True
    activate_account: bool = True


def _db():
    conn = auth.db()
    try:
        init_governance_schema(conn)
        yield conn
    finally:
        conn.close()


def _validate_levels(perms: dict[str, str]) -> None:
    for mod, lv in perms.items():
        if mod not in GOVERNANCE_MODULES:
            raise HTTPException(400, f"módulo inválido: {mod}")
        if lv not in ACCESS_LEVELS:
            raise HTTPException(400, f"nível inválido: {lv}")


@router.get("/modules")
def governance_modules(user: auth.DeskUser = Depends(auth.get_current_user)):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    return {
        "modules": list(GOVERNANCE_MODULES),
        "levels": list(ACCESS_LEVELS),
        "groups": sorted(set(ROLE_GROUPS.values())),
    }


@router.get("/audit")
def governance_audit(
    target_type: str | None = Query(default=None),
    target_id: str | None = Query(default=None),
    limit: int = Query(default=50, ge=1, le=200),
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    return {"events": list_audit(conn, target_type=target_type, target_id=target_id, limit=limit)}


@router.get("/users/{username}/meta")
def user_meta(
    username: str,
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    meta = get_user_meta(conn, username)
    if not meta:
        row = auth._user_row(username)
        if not row:
            raise HTTPException(404, "user not found")
        meta = ensure_user_meta(conn, username, row["role"])
        conn.commit()
    return {"meta": meta}


@router.get("/users/stats")
def users_stats(
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    rows = conn.execute(
        "SELECT username, role, active FROM desk_users"
    ).fetchall()
    total = len(rows)
    active = sum(1 for r in rows if r["active"])
    frozen = total - active
    super_admin = sum(1 for r in rows if r["role"] == "super_admin")
    return {
        "total": total,
        "active": active,
        "frozen": frozen,
        "super_admin": super_admin,
    }


@router.post("/users/wizard")
def create_user_wizard(
    body: UserWizardRequest,
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    email = body.email.strip().lower()
    if "@" not in email:
        raise HTTPException(400, "email inválido")
    if body.role not in ROLES:
        raise HTTPException(400, "invalid role")
    if body.role == "super_admin" and user.role != "super_admin":
        raise HTTPException(403, "apenas Super Admin pode criar Super Admin")

    perms = (
        body.module_permissions.model_dump()
        if body.module_permissions
        else default_module_permissions(body.role)
    )
    _validate_levels(perms)

    main_group = body.main_group or group_for_role(body.role)
    now = datetime.now(timezone.utc).isoformat()
    active = body.activate_account and body.account_status == "active"
    invite_token = secrets.token_urlsafe(24)

    exists = conn.execute(
        "SELECT 1 FROM desk_users WHERE username = ? OR email = ?",
        (email, email),
    ).fetchone()
    if exists:
        raise HTTPException(409, "utilizador já existe")

    conn.execute(
        """
        INSERT INTO desk_users
        (username, password_hash, role, display_name, email, phone,
         mfa_enabled, totp_secret, totp_enabled, active, created_at, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, NULL, 0, ?, ?, ?)
        """,
        (
            email,
            auth.hash_password(body.password),
            body.role,
            body.display_name.strip(),
            email,
            body.phone,
            1 if body.mfa_required else 0,
            1 if active else 0,
            now,
            now,
        ),
    )

    internal_id = f"LB-{secrets.token_hex(4).upper()}"
    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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
        (
            email,
            internal_id,
            main_group,
            json.dumps(body.secondary_groups),
            body.account_status,
            1 if body.force_password_change else 0,
            1 if body.api_access else 0,
            1 if body.notifications_enabled else 0,
            json.dumps(perms),
            invite_token,
            body.notes,
            now,
            now,
        ),
    )

    actor_label = user.display_name or user.username
    audit = log_audit(
        conn,
        actor_username=user.username,
        actor_role=user.role,
        action="user.created",
        target_type="user",
        target_id=email,
        summary=f"Created by {actor_label}",
        payload={"role": body.role, "internal_id": internal_id},
    )
    conn.commit()

    invite_link = f"https://desk.ligbox.com.br/register.html?invite={invite_token}"
    invite_email_sent = False
    if body.send_invite_email:
        try:
            invite_email_sent = mail_notify.send_email(
                email,
                "Convite Ligbox Ops Desk",
                f"Olá {body.display_name},\n\nFoi criada a sua conta.\nDefina a sua senha: {invite_link}\n",
            )
        except Exception:
            invite_email_sent = False

    row = conn.execute(
        """
        SELECT u.username, u.role, u.display_name, u.active, u.last_login_at,
               u.created_at, u.updated_at, u.email, u.phone, u.mfa_enabled, u.totp_enabled
        FROM desk_users u WHERE u.username = ?
        """,
        (email,),
    ).fetchone()
    public = auth.user_public_dict(row)
    meta = get_user_meta(conn, email)

    return {
        "user": public,
        "meta": meta,
        "audit": audit,
        "internal_id": internal_id,
        "invite_link": invite_link,
        "invite_email_sent": invite_email_sent if body.send_invite_email else None,
        "message": "Utilizador criado",
    }


@router.post("/users/{username}/freeze")
def freeze_user(
    username: str,
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    target = username.strip().lower() if username.lower() != "root" else "root"
    if target == "root":
        raise HTTPException(400, "não é possível congelar root")
    row = auth._user_row(target)
    if not row:
        raise HTTPException(404, "user not found")
    new_active = not bool(row["active"])
    now = datetime.now(timezone.utc).isoformat()
    conn.execute(
        "UPDATE desk_users SET active = ?, updated_at = ? WHERE username = ?",
        (1 if new_active else 0, now, target),
    )
    meta = ensure_user_meta(conn, target, row["role"])
    upsert_user_meta(conn, target, account_status="active" if new_active else "frozen")
    verb = "Activated" if new_active else "Frozen"
    actor_label = user.display_name or user.username
    audit = log_audit(
        conn,
        actor_username=user.username,
        actor_role=user.role,
        action="user.frozen" if not new_active else "user.activated",
        target_type="user",
        target_id=target,
        summary=f"{verb} by {actor_label}",
    )
    conn.commit()
    return {"user": auth.user_public_dict(auth._user_row(target)), "audit": audit}


@router.post("/users/{username}/reset-password")
def admin_reset_password(
    username: str,
    user: auth.DeskUser = Depends(auth.get_current_user),
    conn=Depends(_db),
):
    if not can_manage_users(user.role):
        raise HTTPException(403, "insufficient permissions")
    target = username.strip().lower() if username.lower() != "root" else "root"
    row = auth._user_row(target)
    if not row:
        raise HTTPException(404, "user not found")
    pwd = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(12))
    now = datetime.now(timezone.utc).isoformat()
    conn.execute(
        "UPDATE desk_users SET password_hash = ?, updated_at = ? WHERE username = ?",
        (auth.hash_password(pwd), now, target),
    )
    actor_label = user.display_name or user.username
    audit = log_audit(
        conn,
        actor_username=user.username,
        actor_role=user.role,
        action="user.password.reset",
        target_type="user",
        target_id=target,
        summary=f"Password reset by {actor_label}",
    )
    conn.commit()
    return {"ok": True, "generated_password": pwd, "audit": audit}
