"""Generate and validate human-readable portal codes.

Format: {number}-{word}-{word} (e.g. "7-crossword-pineapple")

The number is 1-99, and each word is drawn from a curated list of
common, unambiguous English words. This gives roughly 20 bits of
entropy — enough to prevent casual guessing while SPAKE2 protects
against brute-force by eavesdroppers.
"""

from __future__ import annotations

import secrets

# ~200 words: common, easy to spell, easy to pronounce, no homophones.
# Kept short to be memorable over voice/chat.
WORDLIST = [
    "acorn",
    "album",
    "anchor",
    "apple",
    "arrow",
    "atlas",
    "badge",
    "baker",
    "banjo",
    "barrel",
    "basket",
    "beacon",
    "blanket",
    "blizzard",
    "bonfire",
    "breeze",
    "bridge",
    "bucket",
    "buffalo",
    "cabin",
    "cactus",
    "camel",
    "candle",
    "canyon",
    "carpet",
    "castle",
    "cedar",
    "channel",
    "cherry",
    "chestnut",
    "chimney",
    "circuit",
    "cobalt",
    "comet",
    "compass",
    "copper",
    "coral",
    "coyote",
    "cradle",
    "crystal",
    "curtain",
    "cypress",
    "dagger",
    "dolphin",
    "donkey",
    "dragon",
    "drifter",
    "eagle",
    "ember",
    "emerald",
    "falcon",
    "fern",
    "fiddle",
    "flannel",
    "fossil",
    "fountain",
    "galaxy",
    "garden",
    "garlic",
    "gazelle",
    "geyser",
    "glacier",
    "goblet",
    "granite",
    "gravel",
    "griffin",
    "guitar",
    "gypsum",
    "hammer",
    "harbor",
    "harvest",
    "hazel",
    "helmet",
    "horizon",
    "hornet",
    "hurricane",
    "igloo",
    "indigo",
    "island",
    "ivory",
    "jacket",
    "jasmine",
    "jigsaw",
    "jungle",
    "juniper",
    "kayak",
    "kennel",
    "lantern",
    "lasso",
    "lemon",
    "leopard",
    "lighthouse",
    "lizard",
    "lobster",
    "magnet",
    "mammoth",
    "maple",
    "marble",
    "meadow",
    "meteor",
    "mitten",
    "monsoon",
    "mosaic",
    "mustard",
    "narwhal",
    "nebula",
    "nectar",
    "nimbus",
    "nutmeg",
    "obsidian",
    "octopus",
    "olive",
    "orchid",
    "osprey",
    "otter",
    "oyster",
    "paddle",
    "panther",
    "parrot",
    "peacock",
    "pebble",
    "pelican",
    "penguin",
    "pepper",
    "phoenix",
    "pickle",
    "pillow",
    "pineapple",
    "plumber",
    "porcupine",
    "pretzel",
    "prism",
    "pumpkin",
    "python",
    "quartz",
    "rabbit",
    "raccoon",
    "rainbow",
    "raven",
    "ribbon",
    "riddle",
    "rocket",
    "saddle",
    "salmon",
    "sandal",
    "sapphire",
    "scarlet",
    "seagull",
    "shelter",
    "silver",
    "sparrow",
    "spider",
    "squirrel",
    "steeple",
    "summit",
    "sunset",
    "tambourine",
    "tandem",
    "temple",
    "thicket",
    "thistle",
    "thunder",
    "timber",
    "toucan",
    "tractor",
    "trident",
    "trophy",
    "trumpet",
    "tulip",
    "turtle",
    "umbrella",
    "unicorn",
    "valley",
    "velvet",
    "vessel",
    "violet",
    "volcano",
    "walnut",
    "warden",
    "waffle",
    "whistle",
    "willow",
    "wizard",
    "wombat",
    "zenith",
    "zephyr",
    "zipper",
]


def generate_code() -> str:
    """Generate a random portal code like '7-crossword-pineapple'."""
    number = secrets.randbelow(99) + 1  # 1-99
    word1 = secrets.choice(WORDLIST)
    word2 = secrets.choice(WORDLIST)
    # Avoid identical words
    while word2 == word1:
        word2 = secrets.choice(WORDLIST)
    return f"{number}-{word1}-{word2}"


def validate_code(code: str) -> bool:
    """Check that a code looks structurally valid (number-word-word)."""
    parts = code.split("-")
    if len(parts) < 3:
        return False
    try:
        n = int(parts[0])
    except ValueError:
        return False
    return 1 <= n <= 99 and len(parts[1]) > 0 and len(parts[2]) > 0
