"""X-API-Key authentication dependency for the /api/v1 public surface.

Keys are issued by admins via POST /api/admin/api-keys. The full key is
returned once at creation; only its sha256 digest lives in the DB.
Each request presents the raw key in the `X-API-Key` header — we hash
and look it up against `api_keys.key_hash`.

On hit we bump `use_count` + `last_used_at` for audit. The write is
best-effort; if the session bumps into a conflict we swallow the error
and still serve the read.
"""
import hashlib
import secrets
from typing import Annotated

from fastapi import Depends, Header, HTTPException, status
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.db import get_session


def hash_key(raw: str) -> str:
    """sha256 hex digest of the raw key. Safe here because keys are 32+
    bytes of random entropy, not human-generated secrets."""
    return hashlib.sha256(raw.encode("utf-8")).hexdigest()


def generate_key() -> tuple[str, str, str]:
    """Return (raw, prefix, digest). `raw` is shown once to the caller;
    `prefix` is stored alongside the digest so the admin UI can
    identify a key later without revealing it."""
    raw = "ig_" + secrets.token_urlsafe(32)
    return raw, raw[:12], hash_key(raw)


async def require_api_key(
    session: Annotated[AsyncSession, Depends(get_session)],
    x_api_key: Annotated[str | None, Header(alias="X-API-Key")] = None,
) -> dict:
    """FastAPI dependency — returns the matching api_keys row (as dict)
    or raises 401. Use with Depends(require_api_key) on any v1 endpoint."""
    if not x_api_key:
        raise HTTPException(
            status.HTTP_401_UNAUTHORIZED,
            detail="missing X-API-Key header",
            headers={"WWW-Authenticate": "ApiKey"},
        )
    digest = hash_key(x_api_key)
    row = (
        await session.execute(
            text(
                """
                SELECT id::text, label, active
                FROM api_keys
                WHERE key_hash = :h
                """
            ),
            {"h": digest},
        )
    ).first()
    if row is None or not row[2]:
        raise HTTPException(
            status.HTTP_401_UNAUTHORIZED,
            detail="invalid or inactive API key",
        )
    try:
        await session.execute(
            text(
                """
                UPDATE api_keys
                SET last_used_at = now(), use_count = use_count + 1
                WHERE id = :id
                """
            ),
            {"id": row[0]},
        )
        await session.commit()
    except Exception:
        await session.rollback()
    return {"id": row[0], "label": row[1]}
