"""
JWT Token Handler

Sichere JWT-Token-Generierung und -Validierung nach OWASP-Best-Practices.
"""
import os
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any
import jwt
from jwt import PyJWTError
from passlib.context import CryptContext

# JWT Configuration
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
if not JWT_SECRET_KEY:
    raise RuntimeError(
        "JWT_SECRET_KEY environment variable is not set. "
        "Application cannot start without a secure secret key."
    )
JWT_ALGORITHM = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 30
JWT_REFRESH_TOKEN_EXPIRE_DAYS = 7

# Password Hashing (bcrypt mit 12 Rounds)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto", bcrypt__rounds=12)


def hash_password(password: str) -> str:
    """
    Hasht Passwort mit bcrypt

    Args:
        password: Klartext-Passwort

    Returns:
        bcrypt-Hash
    """
    return pwd_context.hash(password)


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    Verifiziert Passwort gegen Hash

    Args:
        plain_password: Eingegebenes Passwort
        hashed_password: Gespeicherter Hash

    Returns:
        True wenn Passwort korrekt
    """
    return pwd_context.verify(plain_password, hashed_password)


def create_access_token(
    data: Dict[str, Any],
    expires_delta: Optional[timedelta] = None
) -> str:
    """
    Erstellt JWT Access Token

    Args:
        data: Payload-Daten (z.B. {"sub": "username", "role": "admin"})
        expires_delta: Custom Expiry-Zeit (default: 30 Min)

    Returns:
        JWT-Token als String

    Token-Claims:
        - sub: Subject (Username)
        - role: User-Rolle
        - exp: Expiry-Timestamp
        - iat: Issued-At-Timestamp
    """
    to_encode = data.copy()
    now = datetime.now(timezone.utc)

    if expires_delta:
        expire = now + expires_delta
    else:
        expire = now + timedelta(minutes=JWT_ACCESS_TOKEN_EXPIRE_MINUTES)

    to_encode.update({
        "exp": expire,
        "iat": now,
        "jti": uuid.uuid4().hex,
    })

    encoded_jwt = jwt.encode(to_encode, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
    return encoded_jwt


def create_refresh_token(data: Dict[str, Any]) -> str:
    """
    Erstellt JWT Refresh Token (längere Gültigkeit)

    Args:
        data: Payload-Daten

    Returns:
        JWT Refresh Token
    """
    to_encode = data.copy()
    now = datetime.now(timezone.utc)
    expire = now + timedelta(days=JWT_REFRESH_TOKEN_EXPIRE_DAYS)

    to_encode.update({
        "exp": expire,
        "iat": now,
        "type": "refresh",  # Token-Type für Unterscheidung
        "jti": uuid.uuid4().hex,
    })

    encoded_jwt = jwt.encode(to_encode, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
    return encoded_jwt


def decode_token(token: str) -> Optional[Dict[str, Any]]:
    """
    Decodiert und validiert JWT-Token

    Args:
        token: JWT-Token

    Returns:
        Payload als Dictionary oder None bei Fehler

    Validiert:
        - Signature
        - Expiry
        - Algorithm
    """
    try:
        payload = jwt.decode(
            token,
            JWT_SECRET_KEY,
            algorithms=[JWT_ALGORITHM]
        )
        return payload
    except PyJWTError:
        return None


def get_password_strength(password: str) -> Dict[str, Any]:
    """
    Prüft Passwort-Stärke gemäß OWASP-Guidelines

    Args:
        password: Zu prüfendes Passwort

    Returns:
        Dictionary mit Stärke-Informationen:
        {
            "is_valid": bool,
            "score": int (0-4),
            "feedback": List[str]
        }

    OWASP-Anforderungen:
        - Min. 8 Zeichen
        - Mindestens 1 Großbuchstabe
        - Mindestens 1 Kleinbuchstabe
        - Mindestens 1 Ziffer
        - Mindestens 1 Sonderzeichen
    """
    feedback = []
    score = 0

    if len(password) < 8:
        feedback.append("Password must be at least 8 characters long")
    else:
        score += 1

    if not any(c.isupper() for c in password):
        feedback.append("Password must contain at least one uppercase letter")
    else:
        score += 1

    if not any(c.islower() for c in password):
        feedback.append("Password must contain at least one lowercase letter")
    else:
        score += 1

    if not any(c.isdigit() for c in password):
        feedback.append("Password must contain at least one digit")
    else:
        score += 1

    special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
    if not any(c in special_chars for c in password):
        feedback.append("Password must contain at least one special character")
    else:
        score += 1

    # Längen-Bonus
    if len(password) >= 12:
        score += 1

    return {
        "is_valid": len(feedback) == 0,
        "score": min(score, 4),  # Max. 4
        "feedback": feedback
    }
