"""Sandbox Área Gerente — produção create-only, nunca apaga existentes (Spec 035)."""

from __future__ import annotations

import os
import re
import secrets
import sqlite3
import uuid
from typing import Any

import httpx

from app import domain_console_sandbox_store, vm112_domains

VM112_API = os.getenv("VM112_API_URL", "http://10.10.10.112:8090")
SANDBOX_DOMAIN_PREFIX = os.getenv("DOMAIN_CONSOLE_SANDBOX_PREFIX", "cenario-")
SANDBOX_ALLOWED_SUFFIX = os.getenv(
    "DOMAIN_CONSOLE_SANDBOX_SUFFIX",
    ".ops.ligbox.com.br",
)
SANDBOX_ENABLED = os.getenv("DOMAIN_CONSOLE_SANDBOX", "1") == "1"

PROTECTED_DOMAINS = vm112_domains.PURGE_BLOCKLIST | frozenset(
    {"ligbox.com.br", "itecnologys.com", "ligbox.com", "myvexx.com"}
)

_SCENARIO_DOMAIN_RE = re.compile(
    rf"^{re.escape(SANDBOX_DOMAIN_PREFIX)}[a-z0-9]([a-z0-9-]{{0,61}}[a-z0-9])?{re.escape(SANDBOX_ALLOWED_SUFFIX)}$",
    re.I,
)


class SandboxError(ValueError):
    pass


class SandboxForbidden(PermissionError):
    pass


def assert_enabled() -> None:
    if not SANDBOX_ENABLED:
        raise SandboxForbidden("DOMAIN_CONSOLE_SANDBOX desactivado")


def assert_no_delete(operation: str) -> None:
    raise SandboxForbidden(
        f"Sandbox create-only: operação «{operation}» proibida — nada existente pode ser apagado"
    )


def is_scenario_domain(domain: str) -> bool:
    return bool(_SCENARIO_DOMAIN_RE.match(domain.lower().strip()))


def suggest_scenario_domain(slug: str | None = None) -> str:
    slug = (slug or uuid.uuid4().hex[:8]).lower()
    slug = re.sub(r"[^a-z0-9-]", "-", slug).strip("-") or uuid.uuid4().hex[:8]
    return f"{SANDBOX_DOMAIN_PREFIX}{slug}{SANDBOX_ALLOWED_SUFFIX}"


def _vm112_onboarding_headers(session_id: str) -> dict[str, str]:
    return {"X-Onboarding-Session": session_id, "Content-Type": "application/json"}


def _extract_account_emails(domain_data: dict[str, Any]) -> list[str]:
    emails: list[str] = []
    for key in ("accounts", "accounts_preview", "emails"):
        raw = domain_data.get(key)
        if isinstance(raw, list):
            for item in raw:
                if isinstance(item, str) and "@" in item:
                    emails.append(item.lower())
                elif isinstance(item, dict):
                    e = item.get("email") or item.get("name")
                    if e and "@" in str(e):
                        emails.append(str(e).lower())
    admin = domain_data.get("admin_email") or domain_data.get("portal_admin")
    if admin and "@" in str(admin):
        emails.append(str(admin).lower())
    return sorted(set(emails))


def fetch_domain_readonly(domain: str) -> dict[str, Any]:
    domain = domain.lower().strip()
    try:
        data = vm112_domains.get_domain(domain)
        return {"ok": True, "source": "vm112", "domain": domain, "data": data}
    except httpx.HTTPError as exc:
        return {
            "ok": False,
            "source": "vm112",
            "domain": domain,
            "error": str(exc),
            "data": None,
        }


def list_domains_readonly(q: str = "") -> dict[str, Any]:
    try:
        data = vm112_domains.list_domains(q)
        return {"ok": True, "source": "vm112", "data": data}
    except httpx.HTTPError as exc:
        return {"ok": False, "source": "vm112", "error": str(exc), "data": None}


def _create_account_vm112(
    *,
    domain: str,
    local_part: str,
    password: str,
    display_name: str,
) -> dict[str, Any]:
    session_id = f"sandbox-{uuid.uuid4().hex}"
    body = {
        "domain": domain,
        "local_part": local_part,
        "password": password,
        "display_name": display_name,
        "use_server_password": False,
        "send_welcome": False,
        "dns_mode": "sandbox",
    }
    with httpx.Client(timeout=180.0) as client:
        r = client.post(
            f"{VM112_API}/api/onboarding/account/create",
            json=body,
            headers=_vm112_onboarding_headers(session_id),
        )
        if r.status_code >= 400:
            try:
                detail = r.json()
            except Exception:
                detail = r.text
            raise SandboxError(f"VM112 account/create HTTP {r.status_code}: {detail}")
        return r.json()


def create_scenario(
    conn: sqlite3.Connection,
    *,
    label: str,
    domain: str | None,
    slug: str | None,
    created_by: str,
    plan_mock: str = "Business",
    admin_password: str | None = None,
) -> dict[str, Any]:
    assert_enabled()
    domain = (domain or suggest_scenario_domain(slug)).lower().strip()

    if domain in PROTECTED_DOMAINS:
        raise SandboxForbidden(f"Domínio protegido: {domain}")

    if not is_scenario_domain(domain):
        raise SandboxForbidden(
            f"Sandbox só cria domínios novos com prefixo «{SANDBOX_DOMAIN_PREFIX}» "
            f"e sufixo «{SANDBOX_ALLOWED_SUFFIX}». Sugestão: {suggest_scenario_domain(slug)}"
        )

    existing = fetch_domain_readonly(domain)
    baseline: list[str] = []
    if existing.get("ok") and existing.get("data"):
        baseline = _extract_account_emails(existing["data"])
        if baseline:
            raise SandboxForbidden(
                f"Domínio {domain} já tem contas em produção — sandbox não altera existentes. "
                "Use outro nome de cenário."
            )

    password = admin_password or secrets.token_urlsafe(12)
    if len(password) < 8:
        raise SandboxError("Senha admin mínimo 8 caracteres")

    vm112_result = _create_account_vm112(
        domain=domain,
        local_part="admin",
        password=password,
        display_name=f"Gerente {label or domain}",
    )

    scenario = domain_console_sandbox_store.create_scenario(
        conn,
        domain=domain,
        label=label or domain,
        baseline_accounts=baseline,
        created_by=created_by,
        plan_mock=plan_mock,
    )

    admin_email = f"admin@{domain}"
    return {
        "scenario": scenario,
        "vm112": vm112_result,
        "credentials": {
            "admin_email": admin_email,
            "password": password,
            "webmail": f"https://mail.{domain}/",
            "console": "https://onboard.ligbox.com.br/admin",
        },
        "guards": {
            "delete_blocked": True,
            "baseline_protected": baseline,
            "mode": "create-only",
        },
    }


def add_account_to_scenario(
    conn: sqlite3.Connection,
    *,
    scenario_id: str,
    local_part: str,
    display_name: str,
    password: str | None = None,
) -> dict[str, Any]:
    assert_enabled()
    scenario = domain_console_sandbox_store.get_scenario(conn, scenario_id)
    if not scenario:
        raise SandboxError("Cenário não encontrado")

    domain = scenario["domain"]
    local_part = re.sub(r"[^a-z0-9._+-]", "", local_part.lower().strip())
    if not local_part:
        raise SandboxError("local_part inválido")

    email = f"{local_part}@{domain}"
    baseline = set(scenario["baseline_accounts"])
    if email in baseline:
        raise SandboxForbidden(
            f"{email} já existia antes do cenário — sandbox não sobrescreve contas existentes"
        )

    if email in scenario["created_accounts"]:
        raise SandboxError(f"{email} já foi criada neste cenário")

    pwd = password or secrets.token_urlsafe(10)
    if len(pwd) < 8:
        raise SandboxError("Senha mínimo 8 caracteres")

    vm112_result = _create_account_vm112(
        domain=domain,
        local_part=local_part,
        password=pwd,
        display_name=display_name or local_part,
    )

    updated = domain_console_sandbox_store.append_created_account(conn, scenario_id, email)
    return {
        "scenario": updated,
        "account": {
            "email": email,
            "password": pwd,
            "display_name": display_name or local_part,
        },
        "vm112": vm112_result,
        "guards": {"delete_blocked": True, "mode": "create-only"},
    }


def build_console_state(conn: sqlite3.Connection, scenario_id: str) -> dict[str, Any]:
    """Estado UI — merge VM112 read + cenário local."""
    scenario = domain_console_sandbox_store.get_scenario(conn, scenario_id)
    if not scenario:
        raise SandboxError("Cenário não encontrado")

    domain = scenario["domain"]
    live = fetch_domain_readonly(domain)
    live_emails = _extract_account_emails(live["data"]) if live.get("data") else []

    accounts = []
    seen = set()
    for email in live_emails or scenario["created_accounts"]:
        if email in seen:
            continue
        seen.add(email)
        is_baseline = email in scenario["baseline_accounts"]
        is_created = email in scenario["created_accounts"]
        accounts.append(
            {
                "email": email,
                "name": email.split("@")[0].title(),
                "mail_gb": 30,
                "files_gb": 200,
                "nc": True,
                "active": True,
                "protected": is_baseline,
                "sandbox_created": is_created,
                "can_delete": False,
            }
        )

    return {
        "mode": "live-sandbox",
        "sandbox": True,
        "delete_blocked": True,
        "scenario": scenario,
        "domain": domain,
        "plan": {
            "name": scenario["plan_mock"],
            "price": 549,
            "maxSeats": 25,
            "mailGbDefault": 30,
            "filesGbDefault": 200,
        },
        "billing": {"status": "Sandbox", "nextRenewal": "—"},
        "dmarc": {"spf": True, "dkim": True, "dmarc": True, "score": 95},
        "dns": {
            "mx": live.get("ok", False),
            "aMail": live.get("ok", False),
            "subdomain": f"intranet.{domain}",
        },
        "accounts": accounts,
        "vm112_live": live,
    }
