"""
Helper script to generate a verified, machine-applicable unified patch for Milestone 1.
"""
from __future__ import annotations
import difflib
import subprocess
from pathlib import Path


def mod_zone_types(lines: list[str]) -> list[str]:
    new = list(lines)
    for i, line in enumerate(new):
        if "respawn_y: float" in line:
            new.insert(i + 1, "    max_level: int = 100\n")
            break
    for i, line in enumerate(new):
        if "npc_ids: List[str]" in line:
            new.insert(i + 1, "\n")
            new.insert(i + 2, "    @property\n")
            new.insert(i + 3, "    def display_name(self) -> str:\n")
            new.insert(i + 4, '        """Alias for name attribute to maintain compatibility."""\n')
            new.insert(i + 5, "        return self.name\n")
            break
    return new


def mod_zone_catalog(lines: list[str]) -> list[str]:
    new = list(lines)
    # Add portal_sanctuary_to_hideout to zone_boundless_sanctuary portals
    for i, line in enumerate(new):
        if '"portal_sanctuary_to_sandstorm": ZonePortal(' in line:
            # find end of this portal definition (min_level=20,)
            for j in range(i, len(new)):
                if "min_level=20," in new[j]:
                    portal_chunk = [
                        "                ),\n",
                        '                "portal_sanctuary_to_hideout": ZonePortal(\n',
                        '                    portal_id="portal_sanctuary_to_hideout",\n',
                        '                    name="Lối Vào: Tiên Phủ Động Thiên",\n',
                        '                    source_zone_id="zone_boundless_sanctuary",\n',
                        "                    source_x=380.0,\n",
                        "                    source_y=0.0,\n",
                        '                    target_zone_id="zone_player_hideout",\n',
                        "                    target_x=0.0,\n",
                        "                    target_y=0.0,\n",
                        "                    min_level=1,\n",
                    ]
                    # replace the closing ), of sandstorm portal
                    new[j + 1 : j + 2] = portal_chunk
                    break
            break

    # Add zone_player_hideout after zone_boundless_sanctuary
    for i, line in enumerate(new):
        if "# 2. Bone Strand (Bờ Đá Tàn Xương)" in line:
            hideout_chunk = [
                "    # 1b. Sanctuary: Player Hideout (Tiên Phủ Động Thiên)\n",
                "    engine.add_zone(\n",
                "        ZoneDefinition(\n",
                '            zone_id="zone_player_hideout",\n',
                '            name="Tiên Phủ Động Thiên (Hideout)",\n',
                "            zone_type=ZoneType.SANCTUARY,\n",
                "            environment=ZoneEnvironment.NORMAL,\n",
                "            min_level=1,\n",
                "            max_players=5000,\n",
                "            bounds_width=800.0,\n",
                "            bounds_height=800.0,\n",
                "            default_spawn_x=0.0,\n",
                "            default_spawn_y=0.0,\n",
                '            respawn_zone_id="zone_player_hideout",\n',
                "            respawn_x=0.0,\n",
                "            respawn_y=0.0,\n",
                "            max_level=100,\n",
                "            waypoints={\n",
                '                "wp_hideout_central": Waypoint(\n',
                '                    waypoint_id="wp_hideout_central",\n',
                '                    name="Trụ Đá Thần Hành: Tiên Phủ Động Thiên",\n',
                '                    zone_id="zone_player_hideout",\n',
                "                    x=0.0,\n",
                "                    y=0.0,\n",
                "                    is_unlocked_by_default=True,\n",
                "                )\n",
                "            },\n",
                "            portals={\n",
                '                "portal_hideout_to_sanctuary": ZonePortal(\n',
                '                    portal_id="portal_hideout_to_sanctuary",\n',
                '                    name="Lối Ra: Doanh Trại Bến Lưu Đày",\n',
                '                    source_zone_id="zone_player_hideout",\n',
                "                    source_x=0.0,\n",
                "                    source_y=-350.0,\n",
                '                    target_zone_id="zone_boundless_sanctuary",\n',
                "                    target_x=360.0,\n",
                "                    target_y=0.0,\n",
                "                    min_level=1,\n",
                "                )\n",
                "            },\n",
                "            npc_ids=[],\n",
                "        )\n",
                "    )\n",
                "\n",
            ]
            new[i:i] = hideout_chunk
            break

    return new


def mod_zone_engine(lines: list[str]) -> list[str]:
    new = []
    i = 0
    while i < len(lines):
        line = lines[i]
        # Clean duplicate get_zone_definition
        if "def get_zone_definition(self, zone_id: str) -> ZoneDefinition:" in line:
            # Skip this definition and its 2 lines
            i += 3
            # Replace next definition's docstring
            if i < len(lines) and "def get_zone_definition(self, zone_id: str) -> Optional[ZoneDefinition]:" in lines[i]:
                new.append(lines[i])
                new.append('        """Safe lookup alias for zone definition; returns None if not found."""\n')
                i += 1
                continue
            continue
        # Clean hardcoded string check
        if 'if zone_id == "zone_player_hideout":' in line:
            # skip this line and the next line (return False)
            i += 2
            continue
        new.append(line)
        i += 1
    return new


def mod_matrix_seeder(lines: list[str]) -> list[str]:
    new = list(lines)
    for i, line in enumerate(new):
        if '"zone_boundless_sanctuary": "ACT_I_HUNGER",' in line:
            new.insert(i + 1, '        "zone_player_hideout": "ACT_I_HUNGER",\n')
            break
    return new


def mod_tests(lines: list[str]) -> list[str]:
    new = list(lines)
    # Add hideout metadata test and player spawning test after test_sanctuary_zone_type_is_sanctuary
    for i, line in enumerate(new):
        if "def test_outer_exploration_zones_are_open_world(" in line:
            chunk = [
                '    def test_hideout_zone_registration_and_metadata(self, zone_engine: ZoneEngine) -> None:\n',
                '        """Verifies that Player Hideout is canonically registered as SANCTUARY with full metadata."""\n',
                '        hideout = zone_engine.get_zone("zone_player_hideout")\n',
                '        assert hideout is not None\n',
                '        assert hideout.zone_type == ZoneType.SANCTUARY\n',
                '        assert hideout.name == "Tiên Phủ Động Thiên (Hideout)"\n',
                '        assert hideout.display_name == "Tiên Phủ Động Thiên (Hideout)"\n',
                '        assert hideout.min_level == 1\n',
                '        assert hideout.max_level == 100\n',
                '        assert "wp_hideout_central" in hideout.waypoints\n',
                '        assert "portal_hideout_to_sanctuary" in hideout.portals\n',
                '\n',
                '    def test_hideout_player_spawning_and_spatial_grid(self, zone_engine: ZoneEngine) -> None:\n',
                '        """Verifies that spawning a player in hideout initializes spatial grid without KeyError."""\n',
                '        player_id = "hero_hideout_occupant"\n',
                '        loc = zone_engine.spawn_player(player_id, "zone_player_hideout")\n',
                '        assert loc.zone_id == "zone_player_hideout"\n',
                '        assert loc.x == 0.0\n',
                '        assert loc.y == 0.0\n',
                '        assert "zone_player_hideout" in zone_engine.zone_spatial_grids\n',
                '        assert zone_engine.zone_spatial_grids["zone_player_hideout"] is not None\n',
                '\n',
            ]
            new[i:i] = chunk
            break

    # Add hideout validation checks in test_validate_monster_spawn_enforcement
    for i, line in enumerate(new):
        if "# Training dummy in Sanctuary / Hideout -> ALLOWED" in line:
            chunk = [
                '        # Hostile monster in Hideout -> REJECTED\n',
                '        can_hideout, h_reason = zone_engine.validate_monster_spawn(\n',
                '            zone_id="zone_player_hideout",\n',
                '            is_dummy=False\n',
                '        )\n',
                '        assert can_hideout is False\n',
                '        assert "zone_player_hideout" in h_reason\n',
                '\n',
            ]
            new[i:i] = chunk
            break

    for i, line in enumerate(new):
        if "# Hostile monster in Outer Map -> ALLOWED" in line:
            chunk = [
                '        can_h_dummy, _ = zone_engine.validate_monster_spawn(\n',
                '            zone_id="zone_player_hideout",\n',
                '            is_dummy=True\n',
                '        )\n',
                '        assert can_h_dummy is True\n',
                '\n',
            ]
            new[i:i] = chunk
            break

    # Add portal traversal and clean get_zone_definition tests before client test
    for i, line in enumerate(new):
        if "def test_client_monster_system_js_no_hostile_mobs_in_hideout(" in line:
            chunk = [
                '    def test_portal_traversal_sanctuary_hideout_roundtrip(self, zone_engine: ZoneEngine) -> None:\n',
                '        """Verifies portal traversal between sanctuary and hideout."""\n',
                '        player_id = "wayfarer_1"\n',
                '        zone_engine.spawn_player(player_id, "zone_boundless_sanctuary")\n',
                '        ok, dest, _ = zone_engine.traverse_portal(player_id, "portal_sanctuary_to_hideout", player_level=1)\n',
                '        assert ok is True\n',
                '        assert dest == "zone_player_hideout"\n',
                '\n',
                '        ok_back, dest_back, _ = zone_engine.traverse_portal(player_id, "portal_hideout_to_sanctuary", player_level=1)\n',
                '        assert ok_back is True\n',
                '        assert dest_back == "zone_boundless_sanctuary"\n',
                '\n',
                '    def test_clean_get_zone_definition(self, zone_engine: ZoneEngine) -> None:\n',
                '        """Verifies get_zone_definition works cleanly without duplicate method declaration."""\n',
                '        hideout = zone_engine.get_zone_definition("zone_player_hideout")\n',
                '        assert hideout is not None\n',
                '        assert hideout.zone_id == "zone_player_hideout"\n',
                '\n',
                '        sanctuary = zone_engine.get_zone_definition("zone_boundless_sanctuary")\n',
                '        assert sanctuary is not None\n',
                '        assert sanctuary.zone_id == "zone_boundless_sanctuary"\n',
                '\n',
                '        missing = zone_engine.get_zone_definition("non_existent_zone_id")\n',
                '        assert missing is None\n',
                '\n',
            ]
            new[i:i] = chunk
            break

    return new


def generate_patch() -> None:
    targets = [
        ("server/world/zone_types.py", mod_zone_types),
        ("server/world/zone_catalog.py", mod_zone_catalog),
        ("server/world/zone_engine.py", mod_zone_engine),
        ("server/world/game_design_matrix_seeder.py", mod_matrix_seeder),
        ("tests/unit/test_zone_monster_spawning_rules.py", mod_tests),
    ]

    full_patch = []
    for rel_path, fn in targets:
        with open(rel_path, "r", encoding="utf-8") as f:
            orig = f.readlines()
        modded = fn(orig)
        diff = list(
            difflib.unified_diff(
                orig,
                modded,
                fromfile="a/" + rel_path,
                tofile="b/" + rel_path,
            )
        )
        full_patch.extend(diff)

    patch_path = Path(".agents/teamwork/explorer_m1_1/m1_server_zone_gating.patch")
    patch_content = "".join(full_patch)
    with open(patch_path, "w", encoding="utf-8", newline="\n") as f:
        f.write(patch_content)
    print(f"Generated patch at {patch_path} ({len(full_patch)} diff lines)")


if __name__ == "__main__":
    generate_patch()
