"""Autoridad local de credenciales de wrapper (Fase 5).

El xamiSDK emite credenciales locales para que los wrappers (PHP/Node/Java) le
hablen al servicio local. Cada credencial es un par (wrapper_key + secret); en disco
solo vive el hash del secret. Revocables individualmente.

Confianza 2 (local): xamiSDK <-> wrapper. Independiente de la confianza con Xami.
Ver docs/arquitectura_xamiSDK.md (§3) y docs/servicios_xamiSDK.md (§4.2).
"""
from __future__ import annotations
import hashlib
import hmac
import json
import os
import secrets
import stat
import time


class Wrappers:
    def __init__(self, base_dir: str):
        self.path = os.path.join(base_dir, "wrappers.json")

    def _load(self) -> dict:
        if not os.path.exists(self.path):
            return {"wrappers": []}
        try:
            with open(self.path) as f:
                return json.load(f)
        except Exception:
            return {"wrappers": []}

    def _save(self, data: dict):
        os.makedirs(os.path.dirname(self.path), exist_ok=True)
        with open(self.path, "w") as f:
            json.dump(data, f, indent=2)
        os.chmod(self.path, stat.S_IRUSR | stat.S_IWUSR)   # 600

    def create(self, name: str) -> dict:
        """Emite una credencial de wrapper. Devuelve {wrapper_key, secret} (el secret
        en claro SOLO aqui). En disco se guarda solo el hash."""
        data = self._load()
        wrapper_key = "wrp_" + secrets.token_hex(8)
        secret = "wsec_" + secrets.token_hex(24)
        rec = {
            "wrapper_key": wrapper_key,
            "secret_hash": _hash(secret),
            "name": name or "wrapper",
            "status": "active",
            "created_at": int(time.time()),
            "last_used_at": None,
        }
        data["wrappers"].append(rec)
        self._save(data)
        return {"wrapper_key": wrapper_key, "secret": secret, "name": rec["name"]}

    def list(self) -> list[dict]:
        """Lista sin exponer el hash del secret."""
        out = []
        for w in self._load()["wrappers"]:
            out.append({k: v for k, v in w.items() if k != "secret_hash"})
        return out

    def revoke(self, wrapper_key: str) -> bool:
        data = self._load()
        found = False
        for w in data["wrappers"]:
            if w["wrapper_key"] == wrapper_key and w["status"] == "active":
                w["status"] = "revoked"
                w["revoked_at"] = int(time.time())
                found = True
        if found:
            self._save(data)
        return found

    def verify(self, wrapper_key: str, secret: str) -> dict | None:
        """Valida wrapper_key + secret. Devuelve el registro (sin hash) si es valido y
        activo, o None. Actualiza last_used_at."""
        data = self._load()
        for w in data["wrappers"]:
            if w["wrapper_key"] == wrapper_key and w["status"] == "active":
                if hmac.compare_digest(w["secret_hash"], _hash(secret)):
                    w["last_used_at"] = int(time.time())
                    self._save(data)
                    return {k: v for k, v in w.items() if k != "secret_hash"}
        return None


def _hash(secret: str) -> str:
    """HMAC-SHA256 con una sal local del propio archivo. Como es local y el secret es
    largo/aleatorio, basta un hash con sal fija por instalacion."""
    return hashlib.sha256(secret.encode()).hexdigest()
