"""
Authentication dependency for API routes.

Supports:
- Local/dev: initial credentials from env or pycharter.cfg; JWT issued and verified in-process.
- Optional auth service: token introspect via HTTP (when auth_service_url + introspect_path set).
"""

from __future__ import annotations

import hmac
import time
from typing import Any

from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

from pycharter.config import (
    get_auth_initial_credentials,
    get_auth_jwt_secret,
    get_auth_service_introspect_path,
    get_auth_service_url,
    is_auth_disabled,
)

# HTTPBearer returns the token from Authorization: Bearer <token>
security = HTTPBearer(auto_error=False)

# Default JWT expiry (seconds)
ACCESS_TOKEN_EXPIRE_SECONDS = 3600  # 1 hour


def _constant_time_compare(a: str, b: str) -> bool:
    """Constant-time string comparison to avoid timing attacks."""
    return hmac.compare_digest(a.encode("utf-8"), b.encode("utf-8"))


def verify_initial_credentials(username: str, password: str) -> bool:
    """Verify username/password against initial credentials (env or cfg). Returns True if valid."""
    creds = get_auth_initial_credentials()
    if not creds:
        return False
    u, p = creds
    return _constant_time_compare(username, u) and _constant_time_compare(password, p)


def _import_jwt():
    """Lazy import jwt so auth module can be imported without PyJWT (e.g. in tests)."""
    try:
        import jwt as _jwt

        return _jwt
    except ImportError:
        raise ImportError(
            "PyJWT is required for JWT auth. Install with: pip install pycharter[api] or pip install PyJWT"
        ) from None


def create_access_token(
    username: str, expires_delta_seconds: int = ACCESS_TOKEN_EXPIRE_SECONDS
) -> str:
    """Create a JWT access token for the given username."""
    jwt = _import_jwt()
    secret = get_auth_jwt_secret()
    if not secret:
        raise ValueError(
            "JWT secret not configured (set PYCHARTER_AUTH_JWT_SECRET or auth.jwt_secret)"
        )
    payload = {
        "sub": username,
        "exp": int(time.time()) + expires_delta_seconds,
        "iat": int(time.time()),
    }
    return jwt.encode(payload, secret, algorithm="HS256")


def verify_jwt(token: str) -> dict[str, Any] | None:
    """Verify JWT and return payload (with 'sub' = username) or None if invalid."""
    try:
        jwt = _import_jwt()
    except ImportError:
        return None
    secret = get_auth_jwt_secret()
    if not secret:
        return None
    try:
        payload = jwt.decode(token, secret, algorithms=["HS256"])
        return payload
    except jwt.PyJWTError:
        return None


async def introspect_token(token: str) -> dict[str, Any]:
    """Call auth service introspect endpoint. Return payload if active, else None."""
    base = get_auth_service_url()
    path = get_auth_service_introspect_path()
    if not base or not path:
        return None
    url = f"{base.rstrip('/')}{path}"
    try:
        import httpx

        async with httpx.AsyncClient() as client:
            # Common pattern: POST with token in body or header
            r = await client.post(
                url,
                data={"token": token},
                headers={"Content-Type": "application/x-www-form-urlencoded"},
                timeout=5.0,
            )
            if r.status_code != 200:
                return None
            data = r.json()
            # Typical introspect: {"active": true, "sub": "username", ...}
            if not data.get("active", False):
                return None
            return data
    except Exception:
        return None


async def get_current_user(
    credentials: HTTPAuthorizationCredentials | None = Depends(security),
) -> dict[str, Any]:
    """
    Resolve current user from Bearer token. Uses JWT verify or auth service introspect.
    When auth is disabled, returns a dummy user so routes do not need to branch.
    """
    if is_auth_disabled():
        return {"username": "anonymous", "auth_disabled": True}

    token = None
    if credentials and credentials.credentials:
        token = credentials.credentials

    if not token:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Not authenticated",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # Prefer JWT verify (no HTTP); fall back to introspect if configured
    payload = verify_jwt(token)
    if payload is None and (
        get_auth_service_url() and get_auth_service_introspect_path()
    ):
        payload = await introspect_token(token)

    if payload is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token",
            headers={"WWW-Authenticate": "Bearer"},
        )

    username = payload.get("sub") or payload.get("username")
    if not username:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token payload",
            headers={"WWW-Authenticate": "Bearer"},
        )

    return {"username": username}
