"""Off-Earth locations — Beast's Lair, War Chamber, Whale, Maze."""

import random

from h2g2.engine.game_object import (
    GameObject, Room, Flag, Direction,
    DirectExit, ConditionalExit, BlockedExit,
)
from h2g2.engine.world import World
from h2g2.engine.state import GameState


# ---- Memorial names ----

MEMORIAL_NAMES = (
    "Gleb Snardfitz, Bibs Trench, Zeke Fitzberry, Elmo Smith, "
    "Arg Vooloo, Boz Scrimble, Nug Trellis, Pib Frumkin"
)


# ---- War dialogue ----

VLHURG_DIALOGUE = [
    (
        '"We have been at war with the G\'Gugvuntt for ten thousand years!" '
        "bellows the Vlhurg leader."
    ),
    (
        '"It was a perfectly innocent remark!" the Vlhurg leader continues. '
        '"Someone said something careless at a diplomatic reception, and the '
        'next thing you know — WAR!"'
    ),
    (
        "The Vlhurg leader pounds the table. \"They said our leader's mother "
        'was a — well, I can\'t repeat it in polite company."'
    ),
]

GGUGVUNT_DIALOGUE = [
    (
        "The G'Gugvuntt leader sneers. \"They started it. Ten thousand years "
        'ago. And we intend to finish it."'
    ),
    (
        '"Their entire civilization is based on a misunderstanding of a '
        'casual remark," the G\'Gugvuntt leader hisses.'
    ),
    (
        '"We shall destroy their home planet," says the G\'Gugvuntt leader, '
        '"as soon as we work out where it is."'
    ),
]


# ---- Object action handlers ----

def _beast_action(state: GameState) -> bool:
    out = state.output
    beast = state.world.get("BEAST")

    if state.prsa == "examine":
        if beast.fset_q(Flag.MUNGEDBIT):
            out.tell(
                "The Ravenous Bugblatter Beast of Traal is fast asleep, "
                "snoring loudly.\n"
            )
        else:
            out.tell(
                "The Ravenous Bugblatter Beast of Traal is a "
                "mind-bogglingly stupid animal. It assumes that if you "
                "can't see it, it can't see you. It is currently very much "
                "awake and very much hungry.\n"
            )
        return True

    if state.prsa in ("tell", "ask", "talk", "say"):
        if beast.fset_q(Flag.MUNGEDBIT):
            out.tell("The Beast is asleep and doesn't hear you.\n")
            return True
        towel = state.world.objects.get("TOWEL")
        if towel and towel.fset_q(Flag.WORNBIT):
            state.flags["name_told"] = True
            out.tell(
                "With the towel wrapped around your head, you boldly tell "
                "the Beast your name. The Beast, being mind-bogglingly "
                "stupid, carefully notes this down.\n"
            )
        else:
            out.tell(
                "The Beast lunges at you! Perhaps talking to it while it "
                "can see you isn't the wisest strategy.\n"
            )
        return True

    if state.prsa in ("attack", "fight", "hit", "kill"):
        if beast.fset_q(Flag.MUNGEDBIT):
            out.tell("It's asleep. Leave it be.\n")
        else:
            state.jigs_up(
                "You charge at the Ravenous Bugblatter Beast of Traal. "
                "This was not one of your better ideas. The Beast eats you "
                "in a single gulp."
            )
        return True

    return False


def _memorial_action(state: GameState) -> bool:
    out = state.output

    if state.prsa == "examine":
        if state.flags.get("beast_defeated"):
            out.tell(
                "A large sandstone memorial. Carved upon it are the names "
                f"of those who have attempted to slay the Beast:\n"
                f"{MEMORIAL_NAMES}, Arthur Dent.\n"
            )
        else:
            out.tell(
                "A large sandstone memorial. Carved upon it are the names "
                f"of those who have attempted to slay the Beast:\n"
                f"{MEMORIAL_NAMES}.\n"
            )
        return True

    if state.prsa in ("carve", "write", "engrave", "scratch"):
        stone = state.world.objects.get("STONE")
        if state.prsi is not stone and not (
            stone and stone.is_held_by(state.protagonist)
        ):
            out.tell("You have nothing to carve with.\n")
            return True
        if not state.flags.get("name_told"):
            out.tell(
                "You carve something on the memorial, but the Beast doesn't "
                "seem to care. Perhaps you need to tell it your name first.\n"
            )
            return True

        # Success — defeat the Beast
        beast = state.world.get("BEAST")
        beast.fset(Flag.MUNGEDBIT)
        state.flags["beast_defeated"] = True
        state.score += 25
        out.tell(
            "You carve your name into the sandstone memorial. The Beast, "
            "seeing your name, realises that since it knows who you are, "
            "it doesn't need to eat you to find out. Satisfied, it curls "
            "up and falls into a deep sleep.\n"
        )
        return True

    return False


def _stone_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "A sharp, flat stone. It looks suitable for carving.\n"
        )
        return True
    return False


def _skeleton_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The skeleton of an unfortunate beasthunter. In its bony grasp "
            "is a small device.\n"
        )
        return True
    if state.prsa in ("search", "look in"):
        nut_com = state.world.get("NUT-COM-INTERFACE")
        if nut_com.parent and nut_com.parent.id == "SKELETON":
            out.tell(
                "You find a Nutrimatic Interface Sub-Processor wedged in "
                "the skeleton's fingers.\n"
            )
        else:
            out.tell("There is nothing else of interest on the skeleton.\n")
        return True
    return False


def _nut_com_interface_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "A small circuit board labeled 'Nutrimatic Interface "
            "Sub-Processor'. It looks like it could be connected to "
            "something.\n"
        )
        return True
    if state.prsa == "take":
        nut_com = state.world.get("NUT-COM-INTERFACE")
        if not state.flags.get("nut_com_scored"):
            state.flags["nut_com_scored"] = True
            state.score += 25
        nut_com.move_to(state.protagonist)
        out.tell("Taken.\n")
        return True
    return False


def _particle_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "A tiny black particle. Printed on it in impossibly small "
            "letters are the words: \"Common sense, Dent, Arthur "
            "(for replacement, order part #31-541).\"\n"
        )
        return True
    if state.prsa == "take":
        if not state.flags.get("particle_scored"):
            state.flags["particle_scored"] = True
            state.score += 25
        state.jigs_up(
            "As you pick up the particle, a massive jolt of electrical "
            "impulses surges through your body. The particle was, it "
            "seems, an integral part of your brain. Removing it was "
            "inadvisable."
        )
        return True
    return False


def _vlhurg_leader_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The Vlhurg leader is a tall, spiny creature with an "
            "expression of permanent outrage.\n"
        )
        return True
    if state.prsa in ("listen", "talk", "ask", "tell"):
        idx = state.flags.get("vlhurg_dialogue_idx", 0)
        if idx < len(VLHURG_DIALOGUE):
            out.tell(VLHURG_DIALOGUE[idx] + "\n")
            state.flags["vlhurg_dialogue_idx"] = idx + 1
        else:
            out.tell(
                "The Vlhurg leader repeats himself angrily, as war "
                "leaders tend to do.\n"
            )
        return True
    return False


def _ggugvunt_leader_action(state: GameState) -> bool:
    out = state.output
    if state.prsa == "examine":
        out.tell(
            "The G'Gugvuntt leader is a squat, reptilian creature with "
            "beady eyes and a malevolent grin.\n"
        )
        return True
    if state.prsa in ("listen", "talk", "ask", "tell"):
        idx = state.flags.get("ggugvunt_dialogue_idx", 0)
        if idx < len(GGUGVUNT_DIALOGUE):
            out.tell(GGUGVUNT_DIALOGUE[idx] + "\n")
            state.flags["ggugvunt_dialogue_idx"] = idx + 1
        else:
            out.tell(
                "The G'Gugvuntt leader just glares at you and mutters "
                "something about revenge.\n"
            )
        return True
    return False


def _conversation_action(state: GameState) -> bool:
    out = state.output
    if state.prsa in ("listen", "examine"):
        out.tell(
            "The two leaders are arguing heatedly about a war that has "
            "raged for ten thousand years, apparently triggered by a "
            "careless remark at a diplomatic reception.\n"
        )
        return True
    return False


# ---- Timed event handlers ----

def _i_beast(state: GameState) -> bool:
    """Escalating danger in the Beast's lair."""
    out = state.output

    if state.here is None or state.here.id != "LAIR":
        return False

    beast = state.world.get("BEAST")
    if beast.fset_q(Flag.MUNGEDBIT):
        return False  # Beast is asleep

    counter = state.flags.get("beast_counter", 0) + 1
    state.flags["beast_counter"] = counter

    towel = state.world.objects.get("TOWEL")
    wearing_towel = towel and towel.fset_q(Flag.WORNBIT)

    if wearing_towel:
        if counter == 1:
            out.tell(
                "\nThe Beast sniffs the air, confused. It can't seem to "
                "see you with the towel over your head.\n"
            )
            return True
        if counter == 2:
            out.tell(
                "\nThe Beast stomps around, bewildered. It knows you're "
                "here somewhere but can't work out where.\n"
            )
            return True
        if counter >= 3:
            out.tell(
                "\nThe Beast growls in frustration and wanders off to "
                "look for easier prey.\n"
            )
            return True
    else:
        if counter == 1:
            out.tell(
                "\nThe Ravenous Bugblatter Beast of Traal eyes you "
                "hungrily.\n"
            )
            return True
        if counter == 2:
            out.tell(
                "\nThe Beast takes a menacing step toward you, drool "
                "dripping from its fangs.\n"
            )
            return True
        if counter >= 3:
            state.jigs_up(
                "The Ravenous Bugblatter Beast of Traal lunges forward "
                "and devours you in a single, terrible gulp."
            )
            return True

    return False


def _i_whale(state: GameState) -> bool:
    """Countdown to whale impact."""
    out = state.output

    if state.here is None or state.here.id != "INSIDE-WHALE":
        return False

    counter = state.flags.get("whale_counter", 0) + 1
    state.flags["whale_counter"] = counter

    if counter == 3:
        out.tell(
            "\nThe rushing sound is getting louder. The whale seems to "
            "be thinking about something.\n"
        )
        return True
    if counter == 6:
        out.tell(
            "\n\"Oh no, not again,\" the whale thinks to itself.\n"
        )
        return True
    if counter == 9:
        out.tell(
            "\nThe rushing sound is now deafening. The walls are "
            "shaking violently.\n"
        )
        return True
    if counter >= 11:
        state.jigs_up(
            "SPLAT! The whale hits the ground at terminal velocity. "
            "You, being inside the whale at the time, also hit the "
            "ground at terminal velocity. This is not survivable."
        )
        return True

    return False


# ---- Room action handlers ----

def _lair_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-ENTER":
        state.flags["beast_counter"] = 0
        state.clock.queue(_i_beast, 4, name="I-BEAST")
        return True

    if rarg == "M-LOOK":
        beast = state.world.get("BEAST")
        if beast.fset_q(Flag.MUNGEDBIT):
            out.tell(
                "You are in the outer lair of the Ravenous Bugblatter "
                "Beast of Traal. The Beast lies curled up, fast asleep. "
                "Exits lead east and southwest.\n"
            )
        else:
            out.tell(
                "You are in the outer lair of the Ravenous Bugblatter "
                "Beast of Traal. The Beast is here, looking hungry. "
                "An exit leads east.\n"
            )
        return True

    return False


def _lair_sw_exit(state: GameState) -> "Room | None":
    """SW exit from LAIR to INNER-LAIR — requires Beast defeated."""
    out = state.output
    beast = state.world.get("BEAST")
    if not beast.fset_q(Flag.MUNGEDBIT):
        out.tell(
            "The Beast blocks your path to the southwest. You'd have "
            "to get past it first.\n"
        )
        return None
    return state.world.get_room("INNER-LAIR")


def _outer_lair_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "A large walled courtyard. Strewn about are a profusion of "
            "gnawed bones bleaching in the sun.\n"
        )
        return True

    return False


def _inner_lair_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "This is the heart of the Beast's lair. The only exit "
            "leads northeast.\n"
        )
        return True

    return False


def _war_chamber_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "You are in the War Chamber of a star battle cruiser. "
            "Two alien leaders sit across from each other at a large "
            "table, arguing furiously.\n"
        )
        return True

    if rarg == "M-ENTER":
        state.flags["vlhurg_dialogue_idx"] = 0
        state.flags["ggugvunt_dialogue_idx"] = 0
        return True

    return False


def _inside_whale_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-ENTER":
        state.flags["whale_counter"] = 0
        state.clock.queue(_i_whale, -1, name="I-WHALE")
        return True

    if rarg == "M-LOOK":
        out.tell(
            "You are in the stomach of a sperm whale. There is a "
            "distant sound of rushing wind.\n"
        )
        return True

    return False


def _maze_action(state: GameState, rarg: str) -> bool:
    out = state.output

    if rarg == "M-LOOK":
        out.tell(
            "A spongy gray maze of twisty little synapses, all alike.\n"
        )
        return True

    if rarg == "M-ENTER":
        state.flags["maze_counter"] = 0
        # Randomly show/hide the particle
        particle = state.world.objects.get("PARTICLE")
        maze = state.world.get_room("MAZE")
        if particle:
            if random.random() < 0.5:
                particle.move_to(maze)
                particle.fclear(Flag.INVISIBLE)
            else:
                particle.move_to(state.world.local_globals)
        return True

    return False


def _maze_exit(state: GameState) -> "Room | None":
    """Maze exits randomly work or fail (40% fail rate)."""
    out = state.output
    if random.random() < 0.4:
        out.tell("You wander around but end up back where you started.\n")
        return None
    return state.world.get_room("MAZE")


# ---- Registration ----

def register(world: World, state: GameState) -> None:
    """Create all off-Earth rooms and objects."""

    # ---- Initialize state flags ----

    state.flags.setdefault("beast_counter", 0)
    state.flags.setdefault("name_told", False)
    state.flags.setdefault("beast_defeated", False)
    state.flags.setdefault("bearings_lost", False)
    state.flags.setdefault("maze_counter", 0)
    state.flags.setdefault("whale_counter", 0)
    state.flags.setdefault("vlhurg_dialogue_idx", 0)
    state.flags.setdefault("ggugvunt_dialogue_idx", 0)
    state.flags.setdefault("nut_com_scored", False)
    state.flags.setdefault("particle_scored", False)

    # ---- Objects ----

    beast = world.register(GameObject(
        "BEAST", desc="Ravenous Bugblatter Beast of Traal",
        synonyms=["beast", "bugblatter", "animal"],
        adjectives=["ravenous", "bugblatter"],
        flags={Flag.NARTICLEBIT, Flag.ACTORBIT, Flag.NDESCBIT},
        action=_beast_action,
    ))

    memorial = world.register(GameObject(
        "MEMORIAL", desc="sandstone memorial",
        synonyms=["memorial", "monument", "sandstone"],
        adjectives=["sandstone", "large"],
        flags={Flag.NDESCBIT},
        action=_memorial_action,
    ))

    stone = world.register(GameObject(
        "STONE", desc="sharp stone",
        synonyms=["stone", "rock"],
        adjectives=["sharp", "flat"],
        flags={Flag.TAKEBIT},
        size=3,
        action=_stone_action,
    ))

    skeleton = world.register(GameObject(
        "SKELETON", desc="skeleton",
        synonyms=["skeleton", "bones", "remains"],
        adjectives=["dead", "beasthunter"],
        flags={Flag.NDESCBIT, Flag.CONTBIT, Flag.SEARCHBIT},
        action=_skeleton_action,
    ))

    nut_com_interface = world.register(GameObject(
        "NUT-COM-INTERFACE", desc="Nutrimatic Interface Sub-Processor",
        synonyms=["interface", "processor", "circuit", "board"],
        adjectives=["nutrimatic", "sub"],
        flags={Flag.TAKEBIT},
        size=2,
        action=_nut_com_interface_action,
    ))

    particle = world.register(GameObject(
        "PARTICLE", desc="black particle",
        synonyms=["particle", "speck"],
        adjectives=["black", "tiny"],
        flags={Flag.TAKEBIT, Flag.INVISIBLE},
        size=1,
        action=_particle_action,
    ))

    vlhurg_leader = world.register(GameObject(
        "VLHURG-LEADER", desc="Vlhurg leader",
        synonyms=["vlhurg", "leader"],
        adjectives=["vlhurg", "tall", "spiny"],
        flags={Flag.NARTICLEBIT, Flag.ACTORBIT, Flag.NDESCBIT},
        action=_vlhurg_leader_action,
    ))

    ggugvunt_leader = world.register(GameObject(
        "GGUGVUNT-LEADER", desc="G'Gugvuntt leader",
        synonyms=["ggugvunt", "g'gugvuntt", "leader"],
        adjectives=["ggugvunt", "squat", "reptilian"],
        flags={Flag.NARTICLEBIT, Flag.ACTORBIT, Flag.NDESCBIT},
        action=_ggugvunt_leader_action,
    ))

    conversation = world.register(GameObject(
        "CONVERSATION", desc="conversation",
        synonyms=["conversation", "argument", "discussion"],
        flags={Flag.NDESCBIT, Flag.INVISIBLE},
        action=_conversation_action,
    ))

    # ---- Rooms ----

    lair = world.register(Room(
        "LAIR", desc="Lair",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_lair_action,
    ))
    beast.move_to(lair)

    outer_lair = world.register(Room(
        "OUTER-LAIR", desc="Outer Lair",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_outer_lair_action,
    ))
    memorial.move_to(outer_lair)
    stone.move_to(outer_lair)

    inner_lair = world.register(Room(
        "INNER-LAIR", desc="Inner Lair",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_inner_lair_action,
    ))
    skeleton.move_to(inner_lair)
    nut_com_interface.move_to(skeleton)

    war_chamber = world.register(Room(
        "WAR-CHAMBER", desc="War Chamber",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_war_chamber_action,
    ))
    vlhurg_leader.move_to(war_chamber)
    ggugvunt_leader.move_to(war_chamber)
    conversation.move_to(war_chamber)

    inside_whale = world.register(Room(
        "INSIDE-WHALE", desc="Inside a Sperm Whale",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_inside_whale_action,
        exits={},  # No exits
    ))

    maze = world.register(Room(
        "MAZE", desc="Maze",
        flags={Flag.RLANDBIT, Flag.ONBIT},
        action=_maze_action,
    ))

    # ---- Connect rooms ----

    lair.exits = {
        Direction.EAST: DirectExit(outer_lair),
        Direction.SW: ConditionalExit(_lair_sw_exit),
    }

    outer_lair.exits = {
        Direction.WEST: DirectExit(lair),
    }

    inner_lair.exits = {
        Direction.NE: DirectExit(lair),
    }

    # War chamber has no standard exits (arrived via dream dispatch)
    war_chamber.exits = {}

    # Maze exits all go through the random fail check
    maze.exits = {
        Direction.NORTH: ConditionalExit(_maze_exit),
        Direction.SOUTH: ConditionalExit(_maze_exit),
        Direction.EAST: ConditionalExit(_maze_exit),
        Direction.WEST: ConditionalExit(_maze_exit),
    }
