import os
import base64
import hashlib
import secrets
from types import SimpleNamespace
from typing import Optional

from fastapi import Depends, Header, HTTPException, Request, status
from jose import JWTError, jwt
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from database import get_db

CENTRAL_AUTH_SECRET = os.getenv("CENTRAL_AUTH_SECRET", "")
STACK_INTERNAL_SECRET = os.getenv("STACK_INTERNAL_SECRET", "")
JWT_ALGORITHM = "HS256"
APP_SLUG = os.getenv("APP_SLUG", "kitchen")


async def get_current_user(request: Request):
    """
    Verify the stack hnf_session cookie.
    Returns a SimpleNamespace so existing routers can use current_user.kitchen_id,
    current_user.is_admin, etc. without modification. kitchen_id is pinned to 1
    (single hotel — remove when stripping multi-tenant scaffolding, see log B1).
    """
    token = request.cookies.get("hnf_session")
    if not token:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")

    try:
        payload = jwt.decode(token, CENTRAL_AUTH_SECRET, algorithms=[JWT_ALGORITHM])
    except JWTError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid session")

    apps = payload.get("apps", [])
    if APP_SLUG not in apps:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="No permission for this app")

    prefix = f"{APP_SLUG}:"
    raw_caps = payload.get("caps", [])
    caps = [c[len(prefix):] for c in raw_caps if isinstance(c, str) and c.startswith(prefix)]

    is_admin = payload.get("is_admin", False)

    return SimpleNamespace(
        id=0,
        email=payload.get("sub", ""),
        username=payload.get("sub", ""),
        name=payload.get("name", ""),
        display_name=payload.get("name", ""),
        is_admin=is_admin,
        is_active=True,
        kitchen_id=1,
        caps=caps,
        role="admin" if is_admin else "user",
    )


def has_cap(user, cap: str) -> bool:
    return user.is_admin or cap in user.caps


def require_cap(cap: str):
    async def checker(user=Depends(get_current_user)):
        if not has_cap(user, cap):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Missing capability: {cap}",
            )
        return user
    return checker


async def get_current_user_from_token(token: str, db: AsyncSession = None):
    """Decode a JWT passed as a query-param (e.g. ?token=...) and return a user or None."""
    try:
        payload = jwt.decode(token, CENTRAL_AUTH_SECRET, algorithms=[JWT_ALGORITHM])
    except JWTError:
        return None

    apps = payload.get("apps", [])
    if APP_SLUG not in apps:
        return None

    prefix = f"{APP_SLUG}:"
    raw_caps = payload.get("caps", [])
    caps = [c[len(prefix):] for c in raw_caps if isinstance(c, str) and c.startswith(prefix)]

    return SimpleNamespace(
        id=0,
        email=payload.get("sub", ""),
        username=payload.get("sub", ""),
        name=payload.get("name", ""),
        display_name=payload.get("name", ""),
        is_admin=payload.get("is_admin", False),
        is_active=True,
        kitchen_id=1,
        caps=caps,
        role="admin" if payload.get("is_admin", False) else "user",
    )


async def get_admin_user(user=Depends(get_current_user)):
    if not user.is_admin:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
    return user


# ─── API KEY AUTH (public/external API) ──────────────────────────────────────

def generate_api_key() -> tuple[str, str, str]:
    raw = secrets.token_bytes(32)
    body = base64.urlsafe_b64encode(raw).decode().rstrip("=")
    full_key = f"fk_{body}"
    key_hash = hashlib.sha256(full_key.encode()).hexdigest()
    key_prefix = full_key[:12] + "..."
    return full_key, key_hash, key_prefix


async def verify_api_key(api_key: str, db: AsyncSession) -> Optional[dict]:
    if not api_key or not api_key.startswith("fk_"):
        return None
    key_hash = hashlib.sha256(api_key.encode()).hexdigest()
    result = await db.execute(
        text("SELECT id, name, key_prefix, is_active FROM api_keys WHERE key_hash = :h"),
        {"h": key_hash},
    )
    row = result.fetchone()
    if not row or not row.is_active:
        return None
    await db.execute(text("UPDATE api_keys SET last_used_at = NOW() WHERE id = :id"), {"id": row.id})
    await db.commit()
    return {"id": row.id, "name": row.name, "key_prefix": row.key_prefix}


async def get_api_key_auth(
    x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
    db: AsyncSession = Depends(get_db),
) -> dict:
    if not x_api_key:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing X-API-Key header")
    info = await verify_api_key(x_api_key, db)
    if not info:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or revoked API key")
    return info
