#!/usr/bin/env bash
# ============================================================================
# Instalador de las skills del Data Lake de Simetrik (estilo CLI).
# Uso (desde tu Claude o terminal):
#   curl -fsSL https://skills.us.simetrik.com/install.sh | bash
#
# Hace: 1) login Google @simetrik (1 clic, cacheado)  2) pide el bundle a la
# Data Lake API (auth-gated: solo @simetrik)  3) lo instala en ~/.claude/skills/.
# Público a propósito: es solo el instalador; el candado está en la API (paso 2).
# ============================================================================
set -euo pipefail

API="${DATALAKE_API:-https://4b1ykjdtge.execute-api.us-east-2.amazonaws.com}"
DEST="${CLAUDE_SKILLS_DIR:-$HOME/.claude/skills}"

echo "▶ Instalador de skills del Data Lake de Simetrik"
echo "  Te autenticarás con tu correo @simetrik (1 clic en el navegador; se cachea)."

python3 - "$API" "$DEST" <<'PY'
import base64, hashlib, http.server, io, json, os, secrets, sys, time
import urllib.error, urllib.parse, urllib.request, webbrowser, zipfile

API, DEST = sys.argv[1], sys.argv[2]
CLIENT_ID = os.environ.get("DATALAKE_GOOGLE_CLIENT_ID",
    "870213695025-tmk32ijbq7kdeahpq3jk3gc6r8u58b41.apps.googleusercontent.com")
CLIENT_SECRET = os.environ.get("DATALAKE_GOOGLE_CLIENT_SECRET", "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")
CACHE = os.path.expanduser("~/.config/datalake/token.json")
AUTH = "https://accounts.google.com/o/oauth2/v2/auth"
TOKEN_URL = "https://oauth2.googleapis.com/token"
PORT = 8765
REDIRECT = f"http://localhost:{PORT}/"


def _b64(b):
    return base64.urlsafe_b64encode(b).rstrip(b"=").decode()


def _post(data):
    req = urllib.request.Request(TOKEN_URL, data=urllib.parse.urlencode(data).encode())
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read())


def _save(tok):
    os.makedirs(os.path.dirname(CACHE), exist_ok=True)
    tok["_exp"] = time.time() + int(tok.get("expires_in", 3600)) - 120
    json.dump(tok, open(CACHE, "w"))
    os.chmod(CACHE, 0o600)


def _cached():
    if not os.path.exists(CACHE):
        return None
    tok = json.load(open(CACHE))
    if tok.get("_exp", 0) > time.time() and tok.get("id_token"):
        return tok["id_token"]
    if tok.get("refresh_token"):
        try:
            new = _post({"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET,
                         "refresh_token": tok["refresh_token"], "grant_type": "refresh_token"})
            new.setdefault("refresh_token", tok["refresh_token"])
            _save(new)
            return new["id_token"]
        except Exception:
            return None
    return None


def _interactive():
    verifier = _b64(secrets.token_bytes(40))
    challenge = _b64(hashlib.sha256(verifier.encode()).digest())
    params = {"client_id": CLIENT_ID, "redirect_uri": REDIRECT, "response_type": "code",
              "scope": "openid email profile", "code_challenge": challenge,
              "code_challenge_method": "S256", "access_type": "offline", "prompt": "consent"}
    url = AUTH + "?" + urllib.parse.urlencode(params)
    # El servidor local recibe el redirect de Google. Si el puerto está ocupado (otra
    # instalación a medias), avisamos claro en vez de un traceback de OSError.
    try:
        srv = http.server.HTTPServer(("localhost", PORT), None)
    except OSError:
        raise SystemExit(
            "✗ El puerto %d está ocupado (¿otra instalación corriendo?). Ciérrala y reintenta." % PORT)
    srv.timeout = 180  # no colgar indefinidamente esperando el redirect

    opened = False
    try:
        opened = webbrowser.open(url)
    except Exception:
        opened = False
    if not opened:
        # entorno sin navegador (CI/SSH/contenedor): no se puede completar el login interactivo
        print("No pude abrir el navegador. Abre esta URL manualmente y autoriza:\n  " + url, flush=True)
        print("(en entornos sin navegador, exporta DATALAKE_ID_TOKEN con tu id_token de Google)", flush=True)
    else:
        print("Abre el navegador y entra con tu correo @simetrik.com…", flush=True)
    box = {}

    class H(http.server.BaseHTTPRequestHandler):
        def do_GET(self):
            box.update(urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query))
            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(b"<h2>Listo. Vuelve a la terminal.</h2>")

        def log_message(self, *a):
            pass

    srv.RequestHandlerClass = H
    srv.handle_request()  # respeta srv.timeout (180s) -> no se cuelga para siempre
    code = box.get("code", [None])[0]
    if not code:
        raise SystemExit(
            "✗ No se recibió la autorización de Google (timeout o cancelado). Reintenta, "
            "o exporta DATALAKE_ID_TOKEN si estás en un entorno sin navegador.")
    tok = _post({"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "code": code,
                 "code_verifier": verifier, "redirect_uri": REDIRECT,
                 "grant_type": "authorization_code"})
    _save(tok)
    return tok["id_token"]


# DATALAKE_ID_TOKEN permite entornos sin navegador (CI/contenedor/SSH): pasas un id_token
# de Google ya obtenido y se salta el login interactivo.
token = os.environ.get("DATALAKE_ID_TOKEN") or _cached() or _interactive()

req = urllib.request.Request(API.rstrip("/") + "/v1/skills",
                             headers={"Authorization": "Bearer " + token})
try:
    with urllib.request.urlopen(req, timeout=60) as r:
        info = json.loads(r.read())
except urllib.error.HTTPError as e:
    if e.code in (401, 403):
        raise SystemExit(
            "✗ La API rechazó tu sesión (%s). Borra el token cacheado y reintenta:\n"
            "    rm -f %s\n  Si persiste, tu cuenta @simetrik no tiene acceso aún." % (e.code, CACHE))
    raise SystemExit("✗ La API devolvió un error %s. Reintenta en un momento o avisa a CloudOps." % e.code)
except urllib.error.URLError as e:
    raise SystemExit("✗ No se pudo contactar la API (%s). ¿Conexión de red / VPN?" % e.reason)

if not info.get("bundle_b64"):
    raise SystemExit("✗ La API no devolvió el bundle de skills (respuesta inesperada).")
print("Descargando skills (versión %s)…" % info.get("version", "?"), flush=True)

blob = base64.b64decode(info["bundle_b64"])  # la API devuelve el bundle en base64 (no presigned URL)
os.makedirs(DEST, exist_ok=True)
with zipfile.ZipFile(io.BytesIO(blob)) as z:
    names = z.namelist()
    z.extractall(DEST)
skills = sorted({n.split("/")[0] for n in names if "/" in n})
print("Instaladas: " + ", ".join(skills))
PY

echo "✅ Skills instaladas en $DEST"
echo "   En Claude úsalas con:  /simetrik-conectar-datos-datalake   y   /simetrik-leer-datalake"
