#!/usr/bin/env python3
"""
keygen_server.py
-----------------
Server-side port of the HMAC key-generation logic from
AlwaysOnTopVocabLearn-drm/keygen.py and license.py.

Unlike keygen.py (a local CLI tool run by hand), this module reads the
DRM secret from an environment variable rather than a hardcoded constant,
so it can run safely on a hosted server. The key format/encoding must
stay byte-for-byte identical to license.py's, or keys issued here will
fail to validate in the app.

This file only ever reads DRM_SECRET from the environment — it never
hardcodes, logs, or prints the secret.
"""

from __future__ import annotations

import base64
import hashlib
import hmac
import os

CURRENT_KEY_VERSION = 1
_SIG_BYTES = 6
_GROUP_LEN = 4


class KeyGenError(Exception):
    """Raised when a key cannot be generated (bad input or missing secret)."""


def _get_secret(version: int) -> bytes:
    # Single-secret-per-deployment for now. If/when CURRENT_KEY_VERSION
    # bumps, this should become a per-version lookup (e.g. DRM_SECRET_V2)
    # mirroring _SECRETS_BY_VERSION in license.py — never remove or change
    # an existing version's secret once keys have been issued under it.
    if version != CURRENT_KEY_VERSION:
        raise KeyGenError(f"No secret configured for key version {version}.")
    secret = os.environ.get("DRM_SECRET")
    if not secret:
        raise KeyGenError("DRM_SECRET environment variable is not set.")
    return secret.encode("utf-8")


def _b32(data: bytes) -> str:
    return base64.b32encode(data).decode("ascii").rstrip("=")


def _group(text: str, group_len: int = _GROUP_LEN) -> str:
    return "-".join(text[i:i + group_len] for i in range(0, len(text), group_len))


def generate_key(email: str, version: int = CURRENT_KEY_VERSION) -> str:
    """Generate a license key for *email* under *version*.

    Mirrors license.generate_key exactly: version_byte + email_len_byte +
    email_bytes + truncated HMAC-SHA256 signature, base32-encoded and
    grouped with hyphens.
    """
    secret = _get_secret(version)

    email_norm = email.strip().lower().encode("utf-8")
    if not email_norm or len(email_norm) > 255:
        raise KeyGenError("Email must be 1-255 bytes once UTF-8 encoded.")

    header = bytes([version, len(email_norm)]) + email_norm
    signature = hmac.new(secret, header, hashlib.sha256).digest()[:_SIG_BYTES]
    payload = header + signature
    return _group(_b32(payload))
