"""Códigos de autorização purge — domínios protegidos (Spec 017 extensão)."""

from __future__ import annotations

import secrets
import sqlite3
import uuid
from datetime import datetime, timedelta, timezone
from typing import Any

from app import auth, vm112_domains

DEFAULT_TTL_HOURS = int(__import__("os").getenv("PURGE_AUTH_CODE_TTL_HOURS", "24"))


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


def _now_iso() -> str:
    return _now().isoformat()


def init_purge_auth_schema(conn: sqlite3.Connection) -> None:
    conn.execute(
        """
        CREATE TABLE IF NOT EXISTS purge_auth_codes (
            id TEXT PRIMARY KEY,
            domain TEXT NOT NULL,
            code_hash TEXT NOT NULL,
            note TEXT,
            created_by TEXT NOT NULL,
            created_at TEXT NOT NULL,
            expires_at TEXT NOT NULL,
            used_at TEXT,
            used_by TEXT
        )
        """
    )
    conn.execute(
        """
        CREATE INDEX IF NOT EXISTS idx_purge_auth_codes_domain_active
        ON purge_auth_codes(domain, expires_at, used_at)
        """
    )
    conn.commit()


def normalize_code(code: str) -> str:
    return code.upper().replace("-", "").replace(" ", "").strip()


def format_code_display(raw: str) -> str:
    raw = normalize_code(raw)
    if len(raw) == 8:
        return f"{raw[:4]}-{raw[4:]}"
    return raw


def generate_raw_code() -> str:
    return secrets.token_hex(4).upper()


def requires_extra_auth(domain: str) -> bool:
    return domain.lower().strip() in vm112_domains.PURGE_EXTRA_AUTH_DOMAINS


def create_code(
    conn: sqlite3.Connection,
    domain: str,
    root_password: str,
    created_by: str,
    note: str = "",
    ttl_hours: int | None = None,
) -> dict[str, Any]:
    domain = domain.lower().strip()
    if not vm112_domains.verify_root_password(conn, root_password):
        raise ValueError("Senha Root incorrecta")
    if domain in vm112_domains.PURGE_BLOCKLIST:
        raise ValueError(f"Domínio {domain} está na blocklist absoluta — purge proibido")
    if not requires_extra_auth(domain):
        raise ValueError(f"Domínio {domain} não exige código extra de autorização")

    ttl = ttl_hours if ttl_hours is not None else DEFAULT_TTL_HOURS
    ttl = max(1, min(int(ttl), 168))
    raw = generate_raw_code()
    display = format_code_display(raw)
    now = _now()
    expires = now + timedelta(hours=ttl)
    code_id = uuid.uuid4().hex[:16]

    conn.execute(
        """
        INSERT INTO purge_auth_codes (
            id, domain, code_hash, note, created_by, created_at, expires_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?)
        """,
        (
            code_id,
            domain,
            auth.hash_password(normalize_code(raw)),
            (note or "").strip() or None,
            created_by,
            now.isoformat(),
            expires.isoformat(),
        ),
    )
    conn.commit()
    return {
        "ok": True,
        "id": code_id,
        "domain": domain,
        "code": display,
        "expires_at": expires.isoformat(),
        "ttl_hours": ttl,
        "note": note or None,
    }


def validate_and_consume(
    conn: sqlite3.Connection,
    domain: str,
    code: str,
    used_by: str | None = None,
) -> bool:
    domain = domain.lower().strip()
    if not requires_extra_auth(domain):
        return True
    normalized = normalize_code(code or "")
    if len(normalized) < 6:
        return False

    now = _now_iso()
    rows = conn.execute(
        """
        SELECT id, code_hash FROM purge_auth_codes
        WHERE domain = ? AND used_at IS NULL AND expires_at > ?
        ORDER BY created_at DESC
        """,
        (domain, now),
    ).fetchall()

    for row in rows:
        if auth.verify_password(normalized, row["code_hash"]):
            conn.execute(
                """
                UPDATE purge_auth_codes
                SET used_at = ?, used_by = ?
                WHERE id = ? AND used_at IS NULL
                """,
                (now, used_by, row["id"]),
            )
            conn.commit()
            return True
    return False


def list_codes(conn: sqlite3.Connection, active_only: bool = True, limit: int = 50) -> list[dict[str, Any]]:
    limit = max(1, min(int(limit), 200))
    now = _now_iso()
    if active_only:
        rows = conn.execute(
            """
            SELECT id, domain, note, created_by, created_at, expires_at, used_at, used_by
            FROM purge_auth_codes
            WHERE used_at IS NULL AND expires_at > ?
            ORDER BY created_at DESC
            LIMIT ?
            """,
            (now, limit),
        ).fetchall()
    else:
        rows = conn.execute(
            """
            SELECT id, domain, note, created_by, created_at, expires_at, used_at, used_by
            FROM purge_auth_codes
            ORDER BY created_at DESC
            LIMIT ?
            """,
            (limit,),
        ).fetchall()

    return [
        {
            "id": row["id"],
            "domain": row["domain"],
            "note": row["note"],
            "created_by": row["created_by"],
            "created_at": row["created_at"],
            "expires_at": row["expires_at"],
            "used_at": row["used_at"],
            "used_by": row["used_by"],
            "active": row["used_at"] is None and row["expires_at"] > now,
        }
        for row in rows
    ]
