"""
Spec 037 — Resolve qual conta Cloudflare Ligbox gere um domínio.

Deploy: /opt/ligbox-wizard/backend/app/services/dns_account_registry.py
Config: /opt/ligbox-wizard/dns-accounts.yaml
"""
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

import yaml

from app.config import _PROJECT_ROOT, _parse_token_line
from app.services.cloudflare import CloudflareDNS, CloudflareError

_DEFAULT_CONFIG = _PROJECT_ROOT / "dns-accounts.yaml"


@dataclass(frozen=True)
class LigboxCfAccount:
    id: str
    label: str
    admin_email: str
    cloudflare_account_id: str
    token_file: str

    def load_token(self) -> str:
        path = _PROJECT_ROOT / self.token_file
        if not path.is_file():
            return ""
        for line in path.read_text(encoding="utf-8").splitlines():
            token = _parse_token_line(line)
            if token:
                return token
        return ""

    def client(self) -> CloudflareDNS:
        return CloudflareDNS(token=self.load_token())


@dataclass
class ZoneMatch:
    domain: str
    account: LigboxCfAccount
    zone: dict[str, Any]
    zone_id: str

    @property
    def dns_mode(self) -> str:
        return f"ligbox_cf_{self.account.id}"


def load_accounts(config_path: Path | None = None) -> tuple[list[LigboxCfAccount], list[str]]:
    path = config_path or _DEFAULT_CONFIG
    if not path.is_file():
        return [], []
    data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
    order = list(data.get("probe_order") or [])
    accounts: list[LigboxCfAccount] = []
    for raw in data.get("accounts") or []:
        accounts.append(
            LigboxCfAccount(
                id=str(raw["id"]),
                label=str(raw.get("label") or raw["id"]),
                admin_email=str(raw.get("admin_email") or ""),
                cloudflare_account_id=str(raw.get("cloudflare_account_id") or ""),
                token_file=str(raw.get("token_file") or ""),
            )
        )
    by_id = {a.id: a for a in accounts}
    ordered = [by_id[i] for i in order if i in by_id]
    # append any not listed in probe_order
    seen = {a.id for a in ordered}
    for a in accounts:
        if a.id not in seen:
            ordered.append(a)
    return ordered, order


def resolve_ligbox_zone(
    domain: str,
    config_path: Path | None = None,
) -> ZoneMatch | None:
    """Procura zona nas contas Ligbox por ordem do catálogo."""
    domain = domain.lower().strip().rstrip(".")
    accounts, _ = load_accounts(config_path)
    errors: list[str] = []
    for account in accounts:
        token = account.load_token()
        if not token:
            errors.append(f"{account.id}: token ausente")
            continue
        try:
            cf = account.client()
            zone = cf.get_zone_by_name(domain)
            if zone and zone.get("id"):
                return ZoneMatch(
                    domain=domain,
                    account=account,
                    zone=zone,
                    zone_id=str(zone["id"]),
                )
        except CloudflareError as e:
            errors.append(f"{account.id}: {e}")
    return None


def _load_config_raw(config_path: Path | None = None) -> dict[str, Any]:
    path = config_path or _DEFAULT_CONFIG
    if not path.is_file():
        return {}
    return yaml.safe_load(path.read_text(encoding="utf-8")) or {}


def pick_provision_account(config_path: Path | None = None) -> LigboxCfAccount | None:
    """Conta Ligbox onde o wizard cria zona nova do cliente."""
    data = _load_config_raw(config_path)
    default_id = str(data.get("default_provision_account") or "ibytera")
    accounts, _ = load_accounts(config_path)
    by_id = {a.id: a for a in accounts}
    preferred = by_id.get(default_id)
    if preferred and preferred.load_token():
        return preferred
    for account in accounts:
        if account.load_token():
            return account
    return None


def provision_ligbox_zone(
    domain: str,
    account: LigboxCfAccount | None = None,
    config_path: Path | None = None,
) -> dict[str, Any]:
    """Garante zona do cliente na conta CF Ligbox indicada (cria se necessário)."""
    domain = domain.lower().strip().rstrip(".")
    account = account or pick_provision_account(config_path)
    if not account:
        raise CloudflareError("Nenhuma conta Ligbox com token configurado para provisionar zona.")
    cf = account.client()
    existing = cf.get_zone_by_name(domain)
    if existing:
        return {"zone": existing, "created": False, "account": account}
    acc_id = account.cloudflare_account_id or None
    if acc_id and acc_id.startswith("PREENCHER"):
        acc_id = None
    zone = cf.create_zone(domain, account_id=acc_id)
    return {"zone": zone, "created": True, "account": account}


def resolve_payload(domain: str, config_path: Path | None = None) -> dict[str, Any]:
    match = resolve_ligbox_zone(domain, config_path)
    provision_acct = pick_provision_account(config_path)
    base = {
        "domain": domain.lower().strip().rstrip("."),
        "paths_available": ["provision_ligbox", "byo", "external"],
    }
    if match:
        return {
            **base,
            "matched": True,
            "account_id": match.account.id,
            "account_label": f"{match.account.label} ({match.account.admin_email})",
            "zone_id": match.zone_id,
            "zone_status": match.zone.get("status"),
            "dns_mode": match.dns_mode,
            "can_provision_ligbox": True,
            "provision_account_id": match.account.id,
            "paths_available": ["apply_ligbox", "provision_ligbox", "byo", "external"],
            "message": (
                f"Domínio encontrado na conta Cloudflare {match.account.label}. "
                "Pode aplicar apontamentos Ligbox automaticamente."
            ),
        }
    prov_id = provision_acct.id if provision_acct else None
    return {
        **base,
        "matched": False,
        "dns_mode": f"ligbox_cf_{prov_id}" if prov_id else None,
        "can_provision_ligbox": provision_acct is not None,
        "provision_account_id": prov_id,
        "message": (
            "Domínio novo — o wizard pode criar a zona na Cloudflare gerenciada pela Ligbox "
            f"({provision_acct.label}) ou usar BYO Cloudflare / registrador."
            if provision_acct
            else "Configure tokens Ligbox para provisionar zona, ou use BYO/registrador."
        ),
    }
