import hashlib
import hmac
import secrets
from datetime import datetime, timedelta
from typing import Optional

PASSWORD_ITERATIONS = 120000
TOKEN_BYTES = 32


def utcnow() -> datetime:
    return datetime.utcnow()


def hash_password(password: str, salt: Optional[str] = None) -> str:
    if not salt:
        salt = secrets.token_hex(16)
    derived = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode("utf-8"),
        salt.encode("utf-8"),
        PASSWORD_ITERATIONS,
    ).hex()
    return "pbkdf2_sha256${}${}${}".format(PASSWORD_ITERATIONS, salt, derived)


def verify_password(password: str, password_hash: str) -> bool:
    try:
        algorithm, iterations_text, salt, expected = password_hash.split("$", 3)
        if algorithm != "pbkdf2_sha256":
            return False
        iterations = int(iterations_text)
        derived = hashlib.pbkdf2_hmac(
            "sha256",
            password.encode("utf-8"),
            salt.encode("utf-8"),
            iterations,
        ).hex()
        return hmac.compare_digest(derived, expected)
    except Exception:
        return False


def generate_public_token() -> str:
    return secrets.token_urlsafe(TOKEN_BYTES)


def token_hash(token: str) -> str:
    return hashlib.sha256(token.encode("utf-8")).hexdigest()


def reset_token_expiry(hours: int = 2) -> datetime:
    return utcnow() + timedelta(hours=hours)
