"""User config (~/.kuberoku/config.yaml) read/write and cluster CRUD.

Stores registered clusters, current cluster selection, and per-cluster
overrides (namespace, base_domain, resource_prefix, label_domain, registry).
"""

from __future__ import annotations

import pathlib
from typing import Any

import yaml

from kuberoku.branding import CONFIG_DIR_NAME, USER_CONFIG_FILE
from kuberoku.models import ClusterConfig

_CONFIG_VERSION = 1


def config_dir() -> pathlib.Path:
    """Return the user config directory (~/.kuberoku/)."""
    return pathlib.Path.home() / CONFIG_DIR_NAME


def config_path() -> pathlib.Path:
    """Return the path to ~/.kuberoku/config.yaml."""
    return config_dir() / USER_CONFIG_FILE


def read_config(path: pathlib.Path | None = None) -> dict[str, Any]:
    """Read the user config file. Returns empty structure if not found."""
    p = path or config_path()
    if not p.is_file():
        return {"version": _CONFIG_VERSION, "clusters": {}}
    text = p.read_text(encoding="utf-8").strip()
    if not text:
        return {"version": _CONFIG_VERSION, "clusters": {}}
    data: dict[str, Any] = yaml.safe_load(text)
    if not isinstance(data, dict):
        return {"version": _CONFIG_VERSION, "clusters": {}}
    data.setdefault("version", _CONFIG_VERSION)
    data.setdefault("clusters", {})
    return data


def write_config(data: dict[str, Any], path: pathlib.Path | None = None) -> None:
    """Write the user config file, creating directories as needed."""
    p = path or config_path()
    p.parent.mkdir(parents=True, exist_ok=True)
    data.setdefault("version", _CONFIG_VERSION)
    p.write_text(
        yaml.dump(data, default_flow_style=False, sort_keys=False),
        encoding="utf-8",
    )


def list_clusters(path: pathlib.Path | None = None) -> tuple[ClusterConfig, ...]:
    """Return all configured clusters."""
    data = read_config(path)
    clusters = data.get("clusters", {})
    return tuple(
        ClusterConfig(
            name=name,
            context=entry.get("context", name),
            namespace=entry.get("namespace"),
            base_domain=entry.get("base_domain"),
            resource_prefix=entry.get("resource_prefix"),
            label_domain=entry.get("label_domain"),
            registry=entry.get("registry"),
        )
        for name, entry in clusters.items()
    )


def get_cluster(name: str, path: pathlib.Path | None = None) -> ClusterConfig | None:
    """Get a single cluster config by name. Returns None if not found."""
    data = read_config(path)
    entry = data.get("clusters", {}).get(name)
    if entry is None:
        return None
    return ClusterConfig(
        name=name,
        context=entry.get("context", name),
        namespace=entry.get("namespace"),
        base_domain=entry.get("base_domain"),
        resource_prefix=entry.get("resource_prefix"),
        label_domain=entry.get("label_domain"),
        registry=entry.get("registry"),
    )


def add_cluster(
    name: str,
    context: str,
    *,
    namespace: str | None = None,
    default: bool = False,
    base_domain: str | None = None,
    resource_prefix: str | None = None,
    label_domain: str | None = None,
    registry: str | None = None,
    path: pathlib.Path | None = None,
) -> ClusterConfig:
    """Add a cluster to config. Raises ValueError if already exists."""
    data = read_config(path)
    clusters = data.setdefault("clusters", {})
    if name in clusters:
        raise ValueError(f"Cluster '{name}' already exists")

    entry: dict[str, Any] = {"context": context}
    if namespace is not None:
        entry["namespace"] = namespace
    if base_domain is not None:
        entry["base_domain"] = base_domain
    if resource_prefix is not None:
        entry["resource_prefix"] = resource_prefix
    if label_domain is not None:
        entry["label_domain"] = label_domain
    if registry is not None:
        entry["registry"] = registry

    clusters[name] = entry

    # Set as default if requested or if it's the first cluster
    if default or "current_cluster" not in data:
        data["current_cluster"] = name

    write_config(data, path)

    return ClusterConfig(
        name=name,
        context=context,
        namespace=namespace,
        base_domain=base_domain,
        resource_prefix=resource_prefix,
        label_domain=label_domain,
        registry=registry,
    )


def remove_cluster(name: str, path: pathlib.Path | None = None) -> bool:
    """Remove a cluster from config. Returns True if removed."""
    data = read_config(path)
    clusters = data.get("clusters", {})
    if name not in clusters:
        return False

    del clusters[name]

    # If we removed the current cluster, clear or update it
    if data.get("current_cluster") == name:
        if clusters:
            data["current_cluster"] = next(iter(clusters))
        else:
            data.pop("current_cluster", None)

    write_config(data, path)
    return True


def get_current_cluster_name(path: pathlib.Path | None = None) -> str | None:
    """Get the name of the current cluster. Returns None if not set."""
    data = read_config(path)
    name = data.get("current_cluster")
    if name is None:
        return None
    # Verify the cluster actually exists
    if name not in data.get("clusters", {}):
        return None
    return str(name)


def set_current_cluster(name: str, path: pathlib.Path | None = None) -> None:
    """Set the current cluster. Raises ValueError if cluster doesn't exist."""
    data = read_config(path)
    if name not in data.get("clusters", {}):
        raise ValueError(f"Cluster '{name}' not found")
    data["current_cluster"] = name
    write_config(data, path)
