"""Project file (.kuberoku) read/write and directory walking.

The .kuberoku file links a project directory to a Kuberoku app.
Supports simple (single app) and multi-environment formats.
"""

from __future__ import annotations

import pathlib
from dataclasses import dataclass
from typing import Any

import yaml

from kuberoku.branding import PROJECT_FILE_NAME


@dataclass(frozen=True)
class ProjectLink:
    """Resolved app link from a .kuberoku file."""

    app: str
    cluster: str | None = None


def find_project_file(start_dir: pathlib.Path | None = None) -> pathlib.Path | None:
    """Walk up directories to find a .kuberoku file (like .git discovery)."""
    current = (start_dir or pathlib.Path.cwd()).resolve()
    while True:
        candidate = current / PROJECT_FILE_NAME
        if candidate.is_file():
            return candidate
        parent = current.parent
        if parent == current:
            return None
        current = parent


def read_project_file(
    start_dir: pathlib.Path | None = None,
) -> dict[str, Any] | None:
    """Find and parse the .kuberoku file. Returns None if not found."""
    path = find_project_file(start_dir)
    if path is None:
        return None
    text = path.read_text(encoding="utf-8").strip()
    if not text:
        return None
    data: dict[str, Any] = yaml.safe_load(text)
    return data


def resolve_project_link(
    alias: str | None = None,
    start_dir: pathlib.Path | None = None,
) -> ProjectLink | None:
    """Resolve an alias (or default) from the .kuberoku file.

    Resolution:
    - Simple format (app: myapi): alias is ignored, returns that app.
    - Multi-env format (environments block): looks up alias, or default.
    """
    data = read_project_file(start_dir)
    if data is None:
        return None

    # Simple format: just "app: myapi"
    if "app" in data and "environments" not in data:
        return ProjectLink(app=data["app"], cluster=data.get("cluster"))

    # Multi-environment format
    envs = data.get("environments", {})
    if not envs:
        return None

    if alias:
        # Look up the specific alias
        env = envs.get(alias)
        if env:
            return ProjectLink(app=env["app"], cluster=env.get("cluster"))
        return None

    # No alias specified — use default
    default_alias = data.get("default")
    if default_alias and default_alias in envs:
        env = envs[default_alias]
        return ProjectLink(app=env["app"], cluster=env.get("cluster"))

    return None


def write_project_file(
    app: str,
    alias: str | None = None,
    cluster: str | None = None,
    directory: pathlib.Path | None = None,
) -> pathlib.Path:
    """Write or update the .kuberoku file in the given directory.

    - No alias: writes simple format (app: myapi).
    - With alias: writes multi-env format, adding to existing environments.
    """
    target_dir = (directory or pathlib.Path.cwd()).resolve()
    path = target_dir / PROJECT_FILE_NAME

    existing = _read_existing(path)

    if alias is None:
        # Simple format
        data: dict[str, Any] = {"app": app}
        if cluster:
            data["cluster"] = cluster
    else:
        # Multi-environment format
        if existing and "environments" in existing:
            data = existing
        elif existing and "app" in existing:
            # Upgrade from simple to multi-env
            data = {
                "default": alias,
                "environments": {},
            }
        else:
            data = {
                "default": alias,
                "environments": {},
            }

        env_entry: dict[str, Any] = {"app": app}
        if cluster:
            env_entry["cluster"] = cluster
        data.setdefault("environments", {})[alias] = env_entry

        # Set default if not already set
        if "default" not in data:
            data["default"] = alias

    path.write_text(
        yaml.dump(data, default_flow_style=False, sort_keys=False),
        encoding="utf-8",
    )
    return path


def remove_project_file(directory: pathlib.Path | None = None) -> bool:
    """Remove the entire .kuberoku file. Returns True if removed."""
    target_dir = (directory or pathlib.Path.cwd()).resolve()
    path = target_dir / PROJECT_FILE_NAME
    if path.is_file():
        path.unlink()
        return True
    return False


def remove_alias(
    alias: str,
    directory: pathlib.Path | None = None,
) -> bool:
    """Remove a single alias from the .kuberoku file. Returns True if removed."""
    target_dir = (directory or pathlib.Path.cwd()).resolve()
    path = target_dir / PROJECT_FILE_NAME
    existing = _read_existing(path)
    if existing is None:
        return False

    envs = existing.get("environments", {})
    if alias not in envs:
        return False

    del envs[alias]

    # If no environments left, remove the file
    if not envs:
        path.unlink()
        return True

    # If we removed the default, update it
    if existing.get("default") == alias:
        existing["default"] = next(iter(envs))

    path.write_text(
        yaml.dump(existing, default_flow_style=False, sort_keys=False),
        encoding="utf-8",
    )
    return True


def _read_existing(path: pathlib.Path) -> dict[str, Any] | None:
    """Read an existing .kuberoku file, or return None."""
    if not path.is_file():
        return None
    text = path.read_text(encoding="utf-8").strip()
    if not text:
        return None
    result: dict[str, Any] = yaml.safe_load(text)
    return result
