"""
Token helper utilities for the HPP module.

Provides functions for generating and hashing tokens used in:
  - OTP generation and bcrypt hashing
  - Retry token URL-safe generation
  - Generic secure ID generation
"""

import secrets
import string
import logging
import bcrypt

logger = logging.getLogger(__name__)


def generate_otp(length: int = 6) -> str:
    """
    Generate a cryptographically random numeric OTP of the given length.

    Args:
        length: Number of digits (default 6).

    Returns:
        A zero-padded numeric string of the requested length.
    """
    return "".join(secrets.choice(string.digits) for _ in range(length))


def hash_otp(otp: str) -> str:
    """
    Hash a plain-text OTP using bcrypt.

    Args:
        otp: Plain-text numeric OTP string.

    Returns:
        bcrypt hash string (suitable for storing in hpp_otp_tokens.hashed_otp).
    """
    return bcrypt.hashpw(otp.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")


def verify_otp(otp: str, hashed_otp: str) -> bool:
    """
    Verify a plain-text OTP against a stored bcrypt hash.

    Args:
        otp: Plain-text OTP entered by the customer.
        hashed_otp: Stored bcrypt hash from hpp_otp_tokens.

    Returns:
        True if the OTP matches the hash, False otherwise.
    """
    try:
        return bcrypt.checkpw(otp.encode("utf-8"), hashed_otp.encode("utf-8"))
    except Exception as exc:
        logger.error("OTP verification error: %s", exc)
        return False


def generate_retry_token() -> str:
    """
    Generate a URL-safe cryptographically random retry token (32 bytes → 43 chars).

    Returns:
        URL-safe base64 token string.
    """
    return secrets.token_urlsafe(32)


def mask_token(token: str, visible_chars: int = 8) -> str:
    """
    Return a masked version of a token for display in merchant UI.

    Shows the first `visible_chars` characters followed by asterisks.

    Args:
        token: Full token string.
        visible_chars: How many leading characters to expose.

    Returns:
        Masked token string, e.g. "abcd1234****".
    """
    if not token:
        return ""
    if len(token) <= visible_chars:
        return token
    return token[:visible_chars] + "*" * min(8, len(token) - visible_chars)
