"""Password and token hashing only."""
import hashlib
import secrets

import argon2

_hasher = argon2.PasswordHasher(
    time_cost=2,
    memory_cost=65536,
)


def hash_token(token: str) -> str:
    """Hash a token for storage. Use for refresh tokens (no lookup by hash needed)."""
    return _hasher.hash(token)


def digest_token_for_lookup(token: str) -> str:
    """
    Deterministic digest for token lookup (magic links).

    Argon2 produces different hashes each time (random salt), so it cannot be used
    for lookup. SHA256 is deterministic: same token → same digest.
    """
    return hashlib.sha256(token.encode("utf-8")).hexdigest()


def verify_token_hash(plain_token: str, hashed_token: str) -> bool:
    """Verify a plain token against its hash."""
    try:
        _hasher.verify(hashed_token, plain_token)
        return True
    except argon2.exceptions.VerifyMismatchError:
        return False


def hash_password(password: str) -> str:
    """Hash a password with Argon2id for opt-in password-login accounts."""
    return _hasher.hash(password)


def verify_password_hash(password: str, hashed_password: str) -> bool:
    """Verify a password against an Argon2id hash."""
    try:
        return _hasher.verify(hashed_password, password)
    except (argon2.exceptions.VerifyMismatchError, argon2.exceptions.VerificationError):
        return False


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


def generate_refresh_token() -> str:
    """Generate secure refresh token (opaque string)."""
    return generate_secure_token(48)


def generate_magic_link_token() -> str:
    """Generate secure magic link token."""
    return generate_secure_token(32)


def generate_6digit_code() -> str:
    """Generate cryptographically random 6-digit code for TOTP recovery."""
    return f"{secrets.randbelow(1_000_000):06d}"
