from datetime import datetime, timedelta, timezone
from typing import Optional, Any
import secrets
import hashlib

import bcrypt
from jose import JWTError, jwt

from app.core.config import settings


def hash_password(password: str) -> str:
    """Hash a plain-text password using bcrypt."""
    return bcrypt.hashpw(password.encode(), bcrypt.gensalt(12)).decode()


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """Verify a plain-text password against the bcrypt hash."""
    try:
        return bcrypt.checkpw(plain_password.encode(), hashed_password.encode())
    except Exception:
        return False


def create_access_token(subject: Any, role: str, extra_data: Optional[dict] = None) -> str:
    """Create a short-lived JWT access token."""
    expire = datetime.now(timezone.utc) + timedelta(
        minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
    )
    payload = {
        "sub": str(subject),
        "role": role,
        "exp": expire,
        "iat": datetime.now(timezone.utc),
        "type": "access",
    }
    if extra_data:
        payload.update(extra_data)
    return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)


def create_refresh_token(subject: Any) -> str:
    """Create a long-lived JWT refresh token."""
    expire = datetime.now(timezone.utc) + timedelta(
        days=settings.REFRESH_TOKEN_EXPIRE_DAYS
    )
    payload = {
        "sub": str(subject),
        "exp": expire,
        "iat": datetime.now(timezone.utc),
        "type": "refresh",
    }
    return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)


def decode_token(token: str) -> Optional[dict]:
    """Decode and validate a JWT token. Returns payload or None."""
    try:
        payload = jwt.decode(
            token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
        )
        return payload
    except JWTError:
        return None


def generate_secure_token(length: int = 32) -> str:
    """Generate a cryptographically secure random token."""
    return secrets.token_urlsafe(length)


def hash_token(token: str) -> str:
    """Hash a token for safe database storage."""
    return hashlib.sha256(token.encode()).hexdigest()


def generate_stem_id(role_prefix: str = "STU") -> str:
    """Generate a unique portable STEM digital ID.
    Format: STU-XXXX-XXXX (e.g., STU-A3F2-K9PL)
    """
    chars = secrets.token_urlsafe(6).upper().replace("-", "").replace("_", "")[:8]
    return f"{role_prefix}-{chars[:4]}-{chars[4:8]}"


def generate_access_code() -> str:
    """Generate an access code for school/teacher onboarding."""
    parts = [secrets.token_urlsafe(4).upper()[:5] for _ in range(4)]
    return "-".join(parts)
