"""
JWT Token Authentication — lightweight token-based API security.

Provides:
  - create_access_token() — issue tokens (no user DB needed initially)
  - verify_token() — validate and decode
  - jwt_required() — FastAPI dependency for protected endpoints
  - JWTManager — singleton with configurable secret + expiry

Intended for mobile API access.  Works alongside existing open CORS for
desktop GUI (backward compatible — endpoints opt-in to auth).
"""

from __future__ import annotations

import hashlib
import hmac
import json
import os
import time
from base64 import urlsafe_b64encode
from functools import wraps
from typing import Any, Callable, Dict, Optional


class JWTManager:
    """Simple HMAC-SHA256 JWT implementation.  No external dependency."""

    def __init__(self, secret: Optional[str] = None, expiry_sec: int = 86400):
        self._secret = secret or os.environ.get("GAT_JWT_SECRET", self._derive_secret())
        self._expiry_sec = expiry_sec

    @staticmethod
    def _derive_secret() -> str:
        """Derive a machine-local secret from hardware ID + install path."""
        seed = f"gat_{os.environ.get('COMPUTERNAME', '')}_{os.environ.get('USERNAME', '')}"
        try:
            from src.license.validator import _get_hardware_id
            seed = f"{seed}_{_get_hardware_id()}"
        except Exception:
            pass
        return hashlib.sha256(seed.encode()).hexdigest()[:32]

    def create_token(self, payload: Dict[str, Any], expiry_sec: Optional[int] = None) -> str:
        """Create a signed JWT token."""
        header = {"alg": "HS256", "typ": "JWT"}
        now = int(time.time())
        expiry = expiry_sec or self._expiry_sec

        claims = {
            **payload,
            "iat": now,
            "exp": now + expiry,
            "iss": "gold_autotrade",
        }

        header_b64 = urlsafe_b64encode(json.dumps(header).encode()).rstrip(b"=").decode()
        claims_b64 = urlsafe_b64encode(json.dumps(claims).encode()).rstrip(b"=").decode()
        signature = self._sign(f"{header_b64}.{claims_b64}")

        return f"{header_b64}.{claims_b64}.{signature}"

    def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
        """Verify and decode a JWT token. Returns None if invalid/expired."""
        try:
            parts = token.split(".")
            if len(parts) != 3:
                return None

            header_b64, claims_b64, signature = parts
            expected_sig = self._sign(f"{header_b64}.{claims_b64}")
            if not hmac.compare_digest(signature, expected_sig):
                return None

            # Pad for base64 decode
            claims_json = _b64_decode(claims_b64)
            claims = json.loads(claims_json)

            exp = claims.get("exp", 0)
            if exp and int(time.time()) > exp:
                return None  # Expired

            return claims
        except Exception:
            return None

    def _sign(self, message: str) -> str:
        """HMAC-SHA256 sign and base64url encode."""
        sig = hmac.new(self._secret.encode(), message.encode(), hashlib.sha256).digest()
        return urlsafe_b64encode(sig).rstrip(b"=").decode()


def _b64_decode(s: str) -> bytes:
    """Decode unpadded base64url."""
    pad = 4 - len(s) % 4
    if pad != 4:
        s += "=" * pad
    from base64 import urlsafe_b64decode
    return urlsafe_b64decode(s)


# ── Singleton ──

_jwt_manager: Optional[JWTManager] = None


def get_jwt_manager() -> JWTManager:
    global _jwt_manager
    if _jwt_manager is None:
        _jwt_manager = JWTManager()
    return _jwt_manager


# ── Convenience functions ──

def create_access_token(role: str = "user", **extra) -> str:
    """Create a JWT access token with role claim."""
    return get_jwt_manager().create_token({"role": role, **extra})


def verify_token(token: str) -> Optional[Dict[str, Any]]:
    """Verify a JWT token and return claims if valid."""
    return get_jwt_manager().verify_token(token)


# ── FastAPI dependency ──

def jwt_required(optional: bool = False):
    """
    FastAPI dependency: require valid JWT Bearer token.

    Usage:
        @app.get("/api/protected")
        def protected_endpoint(user=Depends(jwt_required())):
            return {"user": user}
    """
    def dependency(authorization: Optional[str] = None) -> Optional[Dict[str, Any]]:
        if not authorization:
            if optional:
                return None
            raise _unauthorized("Missing Authorization header")

        scheme, _, token = authorization.partition(" ")
        if scheme.lower() != "bearer" or not token:
            if optional:
                return None
            raise _unauthorized("Invalid Authorization format")

        claims = verify_token(token.strip())
        if claims is None:
            if optional:
                return None
            raise _unauthorized("Invalid or expired token")

        return claims

    return dependency


def _unauthorized(msg: str):
    """Raise an HTTP 401 — avoids FastAPI import at module level."""
    try:
        from fastapi import HTTPException
        return HTTPException(status_code=401, detail=msg)
    except ImportError:
        raise RuntimeError(f"Unauthorized: {msg}")
