"""Authentication dependencies for FastAPI."""

from __future__ import annotations

import hashlib
import secrets

from fastapi import Header, HTTPException, Request

from hive_compute.config import get_settings
from hive_compute.coordinator.abuse_events import record_abuse_event
from hive_compute.coordinator.database import get_db_ctx
from hive_compute.coordinator.enrollment import consume_enrollment_token
from hive_compute.coordinator.request_cache import get_request_cache
from hive_compute.coordinator.worker_credentials import hash_worker_token
from hive_compute.coordinator.worker_observability import record_worker_auth_failure


def require_client_key(request: Request, authorization: str = Header()) -> dict:
    """Validate client API key. Returns dict with key_hash and optional partner_id."""
    key_hash = resolve_valid_api_key_hash(request, authorization)
    if not key_hash:
        raise HTTPException(status_code=401, detail="Invalid API key")

    # Check if this key belongs to a partner
    partner_id = None
    with get_db_ctx() as db:
        partner = db.execute(
            "SELECT partner_id, status FROM partners WHERE api_key_hash = ?",
            (key_hash,),
        ).fetchone()
        if partner:
            if partner["status"] != "active":
                record_abuse_event(
                    subject_type="partner",
                    subject_id=partner["partner_id"],
                    event_type="inactive_partner_request",
                    severity="medium",
                    detail="Inactive partner attempted API access",
                    dedupe_open=True,
                )
                raise HTTPException(status_code=403, detail="Partner account is inactive")
            partner_id = partner["partner_id"]

    return {"key_hash": key_hash, "partner_id": partner_id}


def require_worker_token(
    request: Request,
    authorization: str = Header(),
    x_worker_id: str = Header(),
) -> str:
    """Validate worker token. Returns worker_id."""
    worker_id = resolve_valid_worker_id(request, authorization=authorization, x_worker_id=x_worker_id)
    if not worker_id:
        record_worker_auth_failure(x_worker_id, "Invalid worker credentials")
        raise HTTPException(status_code=401, detail="Invalid worker credentials")
    return worker_id


def resolve_valid_api_key_hash(request: Request, authorization: str | None = None) -> str | None:
    value = _maybe_bearer(authorization)
    if not value:
        return None

    key_hash = hashlib.sha256(value.encode()).hexdigest()
    cache = get_request_cache(request)
    cache_key = ("api_key", key_hash)
    cached = cache.get(cache_key)
    if cached is not None:
        return key_hash if cached else None

    with get_db_ctx() as db:
        row = db.execute(
            "SELECT key_hash FROM api_keys WHERE key_hash = ? AND revoked_at IS NULL",
            (key_hash,),
        ).fetchone()
    cache[cache_key] = bool(row)
    return key_hash if row else None


def resolve_valid_worker_id(
    request: Request,
    *,
    authorization: str | None = None,
    x_worker_id: str | None = None,
) -> str | None:
    if not x_worker_id:
        return None

    value = _maybe_bearer(authorization)
    if not value:
        return None

    token_hash = hash_worker_token(value)
    cache = get_request_cache(request)
    cache_key = ("worker", x_worker_id, token_hash)
    cached = cache.get(cache_key)
    if cached is not None:
        return x_worker_id if cached else None

    with get_db_ctx() as db:
        row = db.execute(
            """
            SELECT worker_id
            FROM workers
            WHERE worker_id = ?
              AND worker_token = ?
              AND worker_token_revoked_at IS NULL
            """,
            (x_worker_id, token_hash),
        ).fetchone()
    cache[cache_key] = bool(row)
    return x_worker_id if row else None


def authorize_worker_registration(request: Request, worker_id: str) -> dict[str, str]:
    """Authorize worker registration.

    Registration requires a manager-issued enrollment token. The legacy shared
    enrollment secret remains available only when explicitly configured for
    controlled migration or break-glass scenarios. Public self-serve worker
    enrollment can be enabled explicitly for hosted download/install flows.
    """
    settings = get_settings()
    enrollment_token = request.headers.get("X-Enrollment-Token")
    if enrollment_token:
        return consume_enrollment_token(enrollment_token, worker_id)

    legacy_secret = settings.enrollment_secret
    if legacy_secret:
        presented_secret = request.headers.get("X-Enrollment-Secret")
        if presented_secret and secrets.compare_digest(presented_secret, legacy_secret):
            return {"authType": "legacy_enrollment_secret", "worker_id": worker_id}

    if settings.public_worker_enrollment_enabled:
        return {"authType": "public_worker_enrollment", "worker_id": worker_id}

    raise HTTPException(status_code=401, detail="Worker registration requires a valid enrollment token")


def _extract_bearer(authorization: str) -> str:
    if not authorization.startswith("Bearer "):
        raise HTTPException(status_code=401, detail="Authorization must be Bearer token")
    return authorization[7:]


def _maybe_bearer(authorization: str | None) -> str | None:
    if not authorization or not authorization.startswith("Bearer "):
        return None
    return authorization[7:]
