"""
catalogo_module.py — Reader read-only do catálogo CE (espelho em six_catalogo no Supabase).

Stack stdlib-only (urllib, json) — sem dependências externas.

Variáveis de ambiente (lê na primeira chamada, cacheia 60s):
  UNI_DATA_SUPABASE_URL ou SUPABASE_URL         → base URL Supabase
  UNI_DATA_SUPABASE_SERVICE_ROLE ou SUPABASE_*  → service role key

Endpoint exposto:
  GET /api/catalogo?tipo=<t>&busca=<q>&apenas_ativos=<bool>&limit=<n>
  tipo ∈ {procedimento, consulta, experiencia, profissional, pacote, produto, todos}

Mapping CE → público (alinhado ao refactor cortesia→experiência):
  category_name = "Cortesias"           → tipo_publico = "experiencia"
  category_name in (Consulta/Avaliação) → tipo_publico = "consulta"
  depends_on_others = true              → tipo_publico = "protocolo"
  default                               → tipo_publico = "procedimento"
"""

from __future__ import annotations

import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request
from typing import Any

# ─── Config (lazy + simples cache) ─────────────────────────────────────────

_CFG_CACHE: dict[str, Any] = {"loaded_at": 0, "url": "", "key": ""}
_CFG_TTL_SEC = 60


def _config() -> dict[str, str]:
    """Lê env vars uma vez por minuto."""
    now = time.time()
    if now - _CFG_CACHE["loaded_at"] < _CFG_TTL_SEC and _CFG_CACHE["url"]:
        return _CFG_CACHE
    url = (
        os.environ.get("UNI_DATA_SUPABASE_URL")
        or os.environ.get("SUPABASE_URL")
        or ""
    ).rstrip("/")
    key = (
        os.environ.get("UNI_DATA_SUPABASE_SERVICE_ROLE")
        or os.environ.get("UNI_DATA_SUPABASE_ANON_KEY")
        or os.environ.get("SUPABASE_SERVICE_ROLE")
        or os.environ.get("SUPABASE_ANON_KEY")
        or ""
    )
    _CFG_CACHE.update({"loaded_at": now, "url": url, "key": key})
    return _CFG_CACHE


def is_enabled() -> bool:
    cfg = _config()
    return bool(cfg["url"] and cfg["key"])


# ─── HTTP helper (urllib + Accept-Profile pra schema six_catalogo) ─────────

class CatalogoError(Exception):
    pass


def _get(table: str, query_params: dict[str, str], timeout: int = 15) -> list[dict[str, Any]]:
    cfg = _config()
    if not cfg["url"] or not cfg["key"]:
        raise CatalogoError(
            "Credenciais Supabase ausentes (UNI_DATA_SUPABASE_URL/SERVICE_ROLE ou SUPABASE_*)."
        )

    qs = urllib.parse.urlencode(query_params, safe=",")
    url = f"{cfg['url']}/rest/v1/{table}?{qs}"

    req = urllib.request.Request(
        url,
        headers={
            "apikey": cfg["key"],
            "Authorization": f"Bearer {cfg['key']}",
            "Accept": "application/json",
            "Accept-Profile": "six_catalogo",
        },
        method="GET",
    )

    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            body = resp.read().decode("utf-8")
            return json.loads(body) if body else []
    except urllib.error.HTTPError as e:
        err = ""
        try:
            err = e.read().decode("utf-8", errors="ignore")[:300]
        except Exception:
            pass
        raise CatalogoError(f"HTTP {e.code} em {table}: {err}") from e
    except urllib.error.URLError as e:
        raise CatalogoError(f"URLError em {table}: {e.reason}") from e


# ─── Classificação CE → tipo público ───────────────────────────────────────

def _classificar(category_name: str | None, depends_on_others: bool) -> str:
    cat = (category_name or "").strip().lower()
    if cat in ("consulta", "avaliação", "avaliacao"):
        return "consulta"
    if cat in ("cortesias", "cortesia"):
        return "experiencia"
    if depends_on_others:
        return "protocolo"
    return "procedimento"


_TIPOS_VALIDOS = {
    "procedimento",
    "consulta",
    "experiencia",
    "protocolo",
    "profissional",
    "pacote",
    "produto",
    "todos",
}


# ─── API pública (chamada pelo server.py) ──────────────────────────────────

def listar(
    tipo: str = "todos",
    busca: str = "",
    apenas_ativos: bool = True,
    limit: int = 50,
) -> dict[str, Any]:
    """
    Retorna {tipo, total, items: [...]} pronto pra serializar JSON.

    Levanta CatalogoError em caso de problema (capture no handler).
    """
    tipo_norm = (tipo or "todos").strip().lower()
    if tipo_norm not in _TIPOS_VALIDOS:
        raise CatalogoError(f"Tipo inválido: '{tipo_norm}'. Use: {', '.join(sorted(_TIPOS_VALIDOS))}.")

    limit = max(1, min(int(limit or 50), 500))
    busca_norm = (busca or "").strip()

    # ── Profissionais ──
    if tipo_norm == "profissional":
        params = {"select": "ce_id,name,short_name,specialty,registro,category,active", "limit": str(limit)}
        if apenas_ativos:
            params["ativo_ce"] = "eq.true"
        if busca_norm:
            params["or"] = f"(name.ilike.*{busca_norm}*,short_name.ilike.*{busca_norm}*)"
        rows = _get("profissionais", params)
        return {"tipo": tipo_norm, "total": len(rows), "items": rows}

    # ── Pacotes ──
    if tipo_norm == "pacote":
        params = {"select": "ce_id,description,cents,active", "limit": str(limit)}
        if apenas_ativos:
            params["ativo_ce"] = "eq.true"
        if busca_norm:
            params["description"] = f"ilike.*{busca_norm}*"
        rows = _get("pacotes", params)
        for r in rows:
            r["preco_reais"] = (r.get("cents") or 0) / 100
        return {"tipo": tipo_norm, "total": len(rows), "items": rows}

    # ── Produtos ──
    if tipo_norm == "produto":
        params = {"select": "ce_id,description,sku,cents,active", "limit": str(limit)}
        if apenas_ativos:
            params["ativo_ce"] = "eq.true"
        if busca_norm:
            params["description"] = f"ilike.*{busca_norm}*"
        rows = _get("produtos", params)
        for r in rows:
            r["preco_reais"] = (r.get("cents") or 0) / 100
        return {"tipo": tipo_norm, "total": len(rows), "items": rows}

    # ── Procedimentos/Consultas/Experiências/Protocolos/Todos ──
    # Lê tudo de procedimentos e classifica em código (espelha view v_catalogo_publico)
    params = {
        "select": "ce_id,description,category_name,depends_on_others,cents,duration_min,active,enabled_professional_ids",
        "limit": str(limit * 4 if tipo_norm != "todos" else limit),  # margem pra filtro local
    }
    if apenas_ativos:
        params["ativo_ce"] = "eq.true"
    if busca_norm:
        params["description"] = f"ilike.*{busca_norm}*"

    rows = _get("procedimentos", params)
    classificados: list[dict[str, Any]] = []
    for r in rows:
        cls = _classificar(r.get("category_name"), bool(r.get("depends_on_others")))
        r["tipo_publico"] = cls
        r["preco_reais"] = (r.get("cents") or 0) / 100
        classificados.append(r)

    if tipo_norm != "todos":
        classificados = [i for i in classificados if i.get("tipo_publico") == tipo_norm]

    classificados = classificados[:limit]
    return {"tipo": tipo_norm, "total": len(classificados), "items": classificados}


def sync_status() -> dict[str, Any]:
    """Retorna info do último sync_runs (pra debug/UI mostrar 'última sync')."""
    try:
        rows = _get(
            "sync_runs",
            {"select": "*", "order": "started_at.desc", "limit": "1"},
        )
        if not rows:
            return {"available": False, "message": "Sync nunca rodou — renove token CE."}
        return {"available": True, "last": rows[0]}
    except CatalogoError as e:
        return {"available": False, "error": str(e)}
