"""Player housing — personal zones."""

import logging
import tomllib
from pathlib import Path

from mudlib.entity import Entity
from mudlib.portal import Portal
from mudlib.thing import Thing
from mudlib.things import spawn_thing, thing_templates
from mudlib.zone import Zone
from mudlib.zones import get_zone, register_zone

log = logging.getLogger(__name__)

# Default home zone dimensions
HOME_WIDTH = 9
HOME_HEIGHT = 9
HOME_SPAWN_X = 4
HOME_SPAWN_Y = 4

# Where player zone files live
_zones_dir: Path | None = None


def init_housing(zones_dir: Path) -> None:
    """Set the directory for player zone files."""
    global _zones_dir
    _zones_dir = zones_dir
    _zones_dir.mkdir(parents=True, exist_ok=True)


def _home_zone_name(player_name: str) -> str:
    """Return the zone registry name for a player's home."""
    return f"home:{player_name.lower()}"


def _zone_path(player_name: str) -> Path:
    """Return the TOML file path for a player's home zone."""
    if _zones_dir is None:
        raise RuntimeError("Call init_housing() first")
    return _zones_dir / f"{player_name.lower()}.toml"


def create_home_zone(player_name: str) -> Zone:
    """Create a default home zone for a player.

    Creates the zone, registers it, and saves it to disk.

    Args:
        player_name: The player's name

    Returns:
        The newly created Zone
    """
    zone_name = _home_zone_name(player_name)

    # Build terrain — simple grass field with a border
    terrain = []
    for y in range(HOME_HEIGHT):
        row = []
        for x in range(HOME_WIDTH):
            if x == 0 or x == HOME_WIDTH - 1 or y == 0 or y == HOME_HEIGHT - 1:
                row.append("#")  # wall border
            else:
                row.append(".")  # open grass
        terrain.append(row)

    zone = Zone(
        name=zone_name,
        description=f"{player_name}'s home",
        width=HOME_WIDTH,
        height=HOME_HEIGHT,
        toroidal=False,
        terrain=terrain,
        impassable={"#", "^", "~"},
        spawn_x=HOME_SPAWN_X,
        spawn_y=HOME_SPAWN_Y,
        safe=True,
    )

    register_zone(zone_name, zone)
    save_home_zone(player_name, zone)

    return zone


def save_home_zone(player_name: str, zone: Zone) -> None:
    """Save a player's home zone to TOML.

    Args:
        player_name: The player's name
        zone: The zone to save
    """
    path = _zone_path(player_name)

    # Build TOML content
    lines = []
    escaped_name = zone.name.replace("\\", "\\\\").replace('"', '\\"')
    escaped_desc = zone.description.replace("\\", "\\\\").replace('"', '\\"')
    lines.append(f'name = "{escaped_name}"')
    lines.append(f'description = "{escaped_desc}"')
    lines.append(f"width = {zone.width}")
    lines.append(f"height = {zone.height}")
    lines.append(f"toroidal = {'true' if zone.toroidal else 'false'}")
    lines.append(f"spawn_x = {zone.spawn_x}")
    lines.append(f"spawn_y = {zone.spawn_y}")
    lines.append(f"safe = {'true' if zone.safe else 'false'}")
    lines.append("")
    lines.append("[terrain]")
    lines.append("rows = [")
    for row in zone.terrain:
        lines.append(f'    "{"".join(row)}",')
    lines.append("]")
    lines.append("")
    lines.append("[terrain.impassable]")
    tiles = ", ".join(f'"{t}"' for t in sorted(zone.impassable))
    lines.append(f"tiles = [{tiles}]")
    lines.append("")

    # Save furniture (Things in the zone, but not Entities or Portals)
    furniture = [
        obj
        for obj in zone._contents
        if isinstance(obj, Thing)
        and not isinstance(obj, Entity)
        and not isinstance(obj, Portal)
    ]

    for item in furniture:
        lines.append("[[furniture]]")
        lines.append(f'template = "{item.name}"')
        lines.append(f"x = {item.x}")
        lines.append(f"y = {item.y}")
        lines.append("")

    path.write_text("\n".join(lines))


def load_home_zone(player_name: str) -> Zone | None:
    """Load a player's home zone from disk if it exists.

    Also registers it in the zone registry.

    Args:
        player_name: The player's name

    Returns:
        The Zone if it exists on disk, None otherwise
    """
    path = _zone_path(player_name)
    if not path.exists():
        return None

    with open(path, "rb") as f:
        data = tomllib.load(f)

    terrain = [list(row) for row in data.get("terrain", {}).get("rows", [])]
    impassable_list = data.get("terrain", {}).get("impassable", {}).get("tiles", [])
    impassable = set(impassable_list) if impassable_list else {"#", "^", "~"}

    zone = Zone(
        name=data["name"],
        description=data.get("description", ""),
        width=data["width"],
        height=data["height"],
        toroidal=data.get("toroidal", False),
        terrain=terrain,
        impassable=impassable,
        spawn_x=data.get("spawn_x", 0),
        spawn_y=data.get("spawn_y", 0),
        safe=data.get("safe", True),
    )

    # Load furniture
    furniture_list = data.get("furniture", [])
    for item_data in furniture_list:
        template_name = item_data.get("template")
        x = item_data.get("x")
        y = item_data.get("y")

        if template_name not in thing_templates:
            log.warning(
                "Skipping unknown furniture template '%s' in %s",
                template_name,
                player_name,
            )
            continue

        if not isinstance(x, int) or not isinstance(y, int):
            log.warning(
                "Invalid coordinates for furniture '%s' in %s",
                template_name,
                player_name,
            )
            continue

        if not (0 <= x < zone.width and 0 <= y < zone.height):
            log.warning(
                "Out-of-bounds furniture '%s' at (%d,%d) in %s",
                template_name,
                x,
                y,
                player_name,
            )
            continue

        template = thing_templates[template_name]
        spawn_thing(template, zone, x=x, y=y)

    register_zone(zone.name, zone)
    return zone


def get_or_create_home(player_name: str) -> Zone:
    """Get the player's home zone, creating it if it doesn't exist.

    Args:
        player_name: The player's name

    Returns:
        The player's home Zone
    """
    zone_name = _home_zone_name(player_name)

    # Check registry first
    zone = get_zone(zone_name)
    if zone is not None:
        return zone

    # Try loading from disk
    zone = load_home_zone(player_name)
    if zone is not None:
        return zone

    # Create new
    return create_home_zone(player_name)
