"""JWT auth + password hashing (spec §51 Security). API keys/secrets never reach the frontend —
only short-lived JWTs cross the API boundary.

This module stays deliberately DB-free/pure (encode/decode only) -- refresh-token revocation and
rotation (StockLab overhaul, Part A4) live in app/core/token_revocation.py instead, which needs a
live Session. Every token minted here now carries a `jti` claim so a single refresh token can be
individually revoked without that store needing to know anything about JWT encoding."""
from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import Optional
from uuid import uuid4

from jose import JWTError, jwt
from passlib.context import CryptContext

from app.core.config import get_settings

pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")


def hash_password(plain: str) -> str:
    return pwd_context.hash(plain)


def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)


def _create_token(subject: str, expires_delta: timedelta, token_type: str) -> str:
    settings = get_settings()
    now = datetime.now(timezone.utc)
    payload = {
        "sub": subject, "type": token_type, "iat": now, "exp": now + expires_delta,
        # AUDIT FIX (StockLab overhaul, final engineering pass, Part A4): jti (JWT ID, RFC 7519 §4.1.7)
        # so app/core/token_revocation.py can revoke/rotate one token without touching any other
        # token issued to the same user (a coarser "bump a user-wide token_version" design was
        # considered and rejected -- it would invalidate every other active session on a single
        # refresh, which is not what "revoke this token" should do). uuid4 -- unguessable, not
        # derived from the subject or timestamp.
        "jti": str(uuid4()),
    }
    return jwt.encode(payload, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)


def create_access_token(user_id: str) -> str:
    settings = get_settings()
    return _create_token(user_id, timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES), "access")


def create_refresh_token(user_id: str) -> str:
    settings = get_settings()
    return _create_token(user_id, timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS), "refresh")


def decode_token(token: str) -> Optional[dict]:
    settings = get_settings()
    try:
        return jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
    except JWTError:
        return None
