"""
Autenticacion de cajeros.

Prioridad:
  1. Servidor local (http://192.168.101.120:8000/api/auth) — usuario/password del POS
  2. Operator Cloud (icebergecuador.com) — fallback / login por email
  3. Cache offline local (auth_cache.json, TTL 7 dias) — sin conexion

Retornos posibles de authenticate():
  dict  con user/permissions/venues/offline  — exito
  {"error": "no_venue_access"}              — usuario sin acceso a sedes
  {"error": "sin_conexion"}                 — ningun servidor disponible y sin cache
  {"error": "credenciales", "detail": msg}  — servidor respondio con error explicito
  None                                      — credenciales invalidas (respuesta definitiva)
"""
import httpx
import json
import os
import hashlib
from datetime import datetime, timedelta

CLOUD_URL      = "https://icebergecuador.com/Cuenca/Operator/api.php"
APP_KEY        = "iceberg-sync-2026"
CACHE_TTL_DAYS = 7

_CACHE_FILE = os.path.join(os.path.dirname(__file__), "data", "auth_cache.json")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _server_url() -> str:
    config_path = os.path.join(os.path.dirname(__file__), "data", "config.json")
    try:
        with open(config_path, encoding="utf-8") as f:
            return json.load(f).get("server_url", "http://192.168.101.120:8000")
    except Exception:
        return "http://192.168.101.120:8000"


def _load_cache() -> dict:
    try:
        with open(_CACHE_FILE, encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return {}


def _save_cache(username: str, user: dict, permissions: list,
                venues: list, password: str):
    cache = _load_cache()
    cache[username.lower()] = {
        "user":        user,
        "permissions": permissions,
        "venues":      venues,
        "pw_hash":     hashlib.sha256(password.encode()).hexdigest(),
        "cached_at":   datetime.now().isoformat(),
    }
    try:
        os.makedirs(os.path.dirname(_CACHE_FILE), exist_ok=True)
        with open(_CACHE_FILE, "w", encoding="utf-8") as f:
            json.dump(cache, f, indent=2, ensure_ascii=False)
    except Exception:
        pass


def _auth_offline(username: str, password: str) -> dict | None:
    cache = _load_cache()
    entry = cache.get(username.lower())
    if not entry:
        return None
    try:
        if datetime.now() - datetime.fromisoformat(entry["cached_at"]) > timedelta(days=CACHE_TTL_DAYS):
            return None
    except Exception:
        return None
    if entry.get("pw_hash") != hashlib.sha256(password.encode()).hexdigest():
        return None
    return {
        "user":        entry["user"],
        "permissions": entry["permissions"],
        "venues":      entry["venues"],
        "offline":     True,
    }


def _parse_ok(data: dict) -> dict:
    return {
        "user":        data.get("user", {}),
        "permissions": data.get("permissions", []),
        "venues":      data.get("venues", []),
        "offline":     False,
    }


# ---------------------------------------------------------------------------
# API publica
# ---------------------------------------------------------------------------

def tiene_permiso(permisos: list, permiso: str) -> bool:
    return "all" in permisos or permiso in permisos


def authenticate(username: str, password: str, venue_id: str = "") -> dict | None:
    """
    Autentica: servidor local primero, cloud como fallback.

    Flujo:
      1. Servidor local (/api/auth con username) — usuarios del POS
      2. Cloud (email/password) — si local falla o no tiene al usuario
      3. Cache offline — si ambos servidores inalcanzables
    """
    _local_reached = False
    _cloud_reached = False
    _local_cred_error = False
    _server_http_error = False

    # 1. Servidor local (prioridad — acepta username corto tipo "MateoP")
    try:
        r = httpx.post(
            f"{_server_url()}/api/auth",
            json={"username": username, "password": password, "venue_id": venue_id},
            timeout=5,
        )
        _local_reached = True
        if r.status_code == 200:
            data = r.json()
            if data.get("status") == "ok":
                result = _parse_ok(data)
                _save_cache(username, result["user"], result["permissions"],
                            result["venues"], password)
                return result
            msg = data.get("message", "")
            if "sede" in msg.lower() or "acceso" in msg.lower():
                return {"error": "no_venue_access"}
            _local_cred_error = True  # local no lo reconoce; probar cloud
        else:
            _server_http_error = True  # servidor respondio con error HTTP
    except (httpx.ConnectError, httpx.TimeoutException):
        pass  # servidor local caido o timeout
    except Exception:
        pass

    # 2. Cloud (fallback — acepta email completo como "empreraltam@icebergcuenca.com")
    try:
        r = httpx.post(
            CLOUD_URL,
            params={"action": "auth_app"},
            json={"email": username, "password": password, "app_key": APP_KEY},
            timeout=8,
        )
        _cloud_reached = True
        if r.status_code == 200:
            data = r.json()
            if data.get("status") == "ok":
                result = _parse_ok(data)
                _save_cache(username, result["user"], result["permissions"],
                            result["venues"], password)
                return result
            msg = data.get("message", "")
            if "sede" in msg.lower() or "acceso" in msg.lower():
                return {"error": "no_venue_access"}
            if _local_cred_error:
                # Ambos servidores rechazaron: credenciales definitivamente invalidas
                return None
            return None  # cloud rechazo credenciales
        else:
            _server_http_error = True
    except (httpx.ConnectError, httpx.TimeoutException):
        pass  # cloud no disponible
    except Exception:
        pass

    # 3. Cache offline
    cached = _auth_offline(username, password)
    if cached:
        return cached

    # Ninguna fuente funciono — determinar causa
    if not _local_reached and not _cloud_reached:
        return {"error": "sin_conexion"}

    if _server_http_error and not _local_cred_error:
        return {"error": "server_error"}

    return None
