"""
WBS Public API — minimal authenticated proxy to Turso.

Architecture: FastAPI app behind Cloudflare Tunnel + Cloudflare Access.
- GET /          → serves the WBS dashboard HTML
- POST /turso    → validates JWT, allowlists SQL, forwards to Turso HTTP API
- Everything else → 404

Security layers (defense in depth):
  1. Cloudflare Access (email/OAuth auth wall at the edge)
  2. JWT verification (validates Cf-Access-Jwt-Assertion header)
  3. SQL statement allowlist (DML only: SELECT/INSERT/UPDATE/DELETE)
  4. Parameterized queries (handled by dashboard, preserved by proxy)

Auth toggle: AUTH_ENABLED env var. Defaults to true (fail-closed).
Set to "false" only for local testing before Cloudflare Access is configured.
"""
import os
import re
import httpx
from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.responses import FileResponse, JSONResponse
from jose import jwt
from jose.exceptions import JWTError
from functools import lru_cache
from pathlib import Path

# --- Config ---
TURSO_DATABASE_URL = os.environ["TURSO_DATABASE_URL"]
TURSO_AUTH_TOKEN = os.environ["TURSO_AUTH_TOKEN"]

# Auth toggle for local testing. MUST be "true" in production.
AUTH_ENABLED = os.environ.get("AUTH_ENABLED", "true").lower() == "true"
CF_TEAM_DOMAIN = os.environ.get("CF_ACCESS_TEAM_DOMAIN", "")
CF_AUD_TAG = os.environ.get("CF_ACCESS_AUD_TAG", "")

# Convert libsql:// to https:// for HTTP API
TURSO_HTTP_URL = TURSO_DATABASE_URL.replace("libsql://", "https://") + "/v2/pipeline"

STATIC_DIR = Path(__file__).parent / "static"

# DML-only allowlist. Matches first non-whitespace keyword, case-insensitive.
ALLOWED_SQL_PATTERN = re.compile(r"^\s*(SELECT|INSERT|UPDATE|DELETE)\b", re.IGNORECASE)

app = FastAPI(title="WBS Public API", docs_url=None, redoc_url=None)


# --- Auth: verify Cloudflare Access JWT ---
@lru_cache(maxsize=1)
def get_cf_jwks():
    """Fetch and cache Cloudflare Access public keys."""
    url = f"https://{CF_TEAM_DOMAIN}/cdn-cgi/access/certs"
    with httpx.Client(timeout=10) as client:
        return client.get(url).json()


def verify_cf_access(request: Request):
    """Validate the Cf-Access-Jwt-Assertion header. Raises 401 on failure.

    When AUTH_ENABLED=false, returns a stub claim — for local testing ONLY.
    """
    if not AUTH_ENABLED:
        return {"sub": "auth-disabled-local-test"}
    token = request.headers.get("Cf-Access-Jwt-Assertion")
    if not token:
        raise HTTPException(status_code=401, detail="Missing Cloudflare Access token")
    if not CF_TEAM_DOMAIN or not CF_AUD_TAG:
        raise HTTPException(status_code=500, detail="Cloudflare Access not configured")
    try:
        jwks = get_cf_jwks()
        claims = jwt.decode(
            token,
            jwks,
            algorithms=["RS256"],
            audience=CF_AUD_TAG,
            issuer=f"https://{CF_TEAM_DOMAIN}",
        )
        return claims
    except JWTError as e:
        raise HTTPException(status_code=401, detail=f"Invalid token: {e}")


# --- SQL validation ---
def validate_sql_statements(body: dict):
    """Walk the Turso pipeline payload; reject anything that isn't DML."""
    requests = body.get("requests", [])
    for req in requests:
        if req.get("type") != "execute":
            continue  # 'close' and others are fine
        sql = req.get("stmt", {}).get("sql", "")
        if not ALLOWED_SQL_PATTERN.match(sql):
            raise HTTPException(
                status_code=403,
                detail=f"SQL statement not allowed. Only SELECT/INSERT/UPDATE/DELETE permitted. Got: {sql[:80]}",
            )


# --- Routes ---
@app.get("/")
async def serve_dashboard(claims: dict = Depends(verify_cf_access)):
    return FileResponse(STATIC_DIR / "index.html")


@app.post("/turso")
async def turso_proxy(request: Request, claims: dict = Depends(verify_cf_access)):
    body = await request.json()
    validate_sql_statements(body)

    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.post(
            TURSO_HTTP_URL,
            json=body,
            headers={"Authorization": f"Bearer {TURSO_AUTH_TOKEN}"},
        )
    return JSONResponse(content=resp.json(), status_code=resp.status_code)


@app.get("/healthz")
async def healthz():
    """Unauthenticated health check for systemd / monitoring."""
    return {"status": "ok"}
