import base64
import hashlib
import secrets


def new_pkce_verifier(length: int = 43) -> str:
    """
    Creates a new PKCE (Proof Key for Code Exchange) code verifier.

    The code verifier is a cryptographically random string between 43 and 128
    characters long. It is used to generate the code challenge.

    Args:
        length: The desired length of the verifier string. Must be
                between 43 and 128. Defaults to 43.

    Returns:
        A URL-safe random string to be used as the code verifier.

    Raises:
        ValueError: If the length is not within the valid range.
    """
    if not 43 <= length <= 128:
        raise ValueError("Verifier length must be between 43 and 128 characters.")
    # The verifier is a high-entropy cryptographic random string.
    # secrets.token_urlsafe generates a URL-safe text string, which is perfect.
    # The number of bytes of entropy is chosen to produce a string of the desired length.
    # A base64-encoded string is about 4/3 the size of the raw bytes.
    num_bytes = (length * 3) // 4
    return secrets.token_urlsafe(num_bytes)[:length]

def new_pkce_challenge(verifier: str) -> str:
    """
    Creates a PKCE challenge from a given code verifier.

    The challenge is generated by taking the SHA-256 hash of the verifier
    and then Base64 URL-encoding the result.

    Args:
        verifier: The code verifier string created by new_pkce_verifier.

    Returns:
        The Base64 URL-encoded SHA-256 hash of the verifier.
    """
    # 1. Hash the verifier using SHA-256
    # The verifier must be encoded to bytes before hashing. ASCII is fine here.
    sha256_hash = hashlib.sha256(verifier.encode('ascii')).digest()

    # 2. Base64 URL-encode the hash
    # Use RawURLEncoding which omits the trailing '=' padding.
    challenge = base64.urlsafe_b64encode(sha256_hash).rstrip(b'=').decode('ascii')

    return challenge
