"""Tests for user config (~/.kuberoku/config.yaml) CRUD."""

from __future__ import annotations

import pathlib

from kuberoku.config.user import (
    add_cluster,
    get_cluster,
    get_current_cluster_name,
    list_clusters,
    read_config,
    remove_cluster,
    set_current_cluster,
    write_config,
)
from kuberoku.models import ClusterConfig


class TestReadWriteConfig:
    """Tests for read_config / write_config."""

    def test_read_missing_file(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        data = read_config(path)
        assert data["version"] == 1
        assert data["clusters"] == {}

    def test_read_empty_file(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        path.write_text("", encoding="utf-8")
        data = read_config(path)
        assert data["version"] == 1

    def test_write_creates_parent_dirs(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "sub" / "dir" / "config.yaml"
        write_config({"version": 1, "clusters": {}}, path)
        assert path.is_file()

    def test_roundtrip(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        original = {
            "version": 1,
            "current_cluster": "prod",
            "clusters": {
                "prod": {"context": "prod-eks", "namespace": "production"},
            },
        }
        write_config(original, path)
        loaded = read_config(path)
        assert loaded["current_cluster"] == "prod"
        assert loaded["clusters"]["prod"]["context"] == "prod-eks"
        assert loaded["clusters"]["prod"]["namespace"] == "production"


class TestAddCluster:
    """Tests for add_cluster."""

    def test_add_first_cluster_becomes_current(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        result = add_cluster("staging", "staging-gke", namespace="staging-apps", path=path)
        assert isinstance(result, ClusterConfig)
        assert result.name == "staging"
        assert result.context == "staging-gke"
        assert result.namespace == "staging-apps"
        assert get_current_cluster_name(path) == "staging"

    def test_add_second_cluster_no_override(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        add_cluster("prod", "prod-eks", path=path)
        assert get_current_cluster_name(path) == "staging"

    def test_add_with_default_flag(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        add_cluster("prod", "prod-eks", default=True, path=path)
        assert get_current_cluster_name(path) == "prod"

    def test_add_duplicate_raises(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        try:
            add_cluster("staging", "staging-gke-2", path=path)
            raise AssertionError("Should have raised ValueError")
        except ValueError:
            pass

    def test_add_with_all_options(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        result = add_cluster(
            "prod",
            "prod-eks",
            namespace="production",
            base_domain="apps.prod.example.com",
            resource_prefix="myco",
            label_domain="app.myco.com",
            registry="123456.dkr.ecr.us-east-1.amazonaws.com/myco",
            path=path,
        )
        assert result.base_domain == "apps.prod.example.com"
        assert result.resource_prefix == "myco"
        assert result.label_domain == "app.myco.com"
        assert result.registry == "123456.dkr.ecr.us-east-1.amazonaws.com/myco"


class TestRemoveCluster:
    """Tests for remove_cluster."""

    def test_remove_existing(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        removed = remove_cluster("staging", path)
        assert removed is True
        assert list_clusters(path) == ()

    def test_remove_nonexistent(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        removed = remove_cluster("nope", path)
        assert removed is False

    def test_remove_current_switches_to_next(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        add_cluster("prod", "prod-eks", path=path)
        remove_cluster("staging", path)
        assert get_current_cluster_name(path) == "prod"

    def test_remove_last_cluster_clears_current(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        remove_cluster("staging", path)
        assert get_current_cluster_name(path) is None


class TestListClusters:
    """Tests for list_clusters."""

    def test_empty(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        assert list_clusters(path) == ()

    def test_multiple(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", namespace="staging", path=path)
        add_cluster("prod", "prod-eks", namespace="production", path=path)
        clusters = list_clusters(path)
        assert len(clusters) == 2
        names = {c.name for c in clusters}
        assert names == {"staging", "prod"}


class TestGetCluster:
    """Tests for get_cluster."""

    def test_get_existing(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", namespace="staging-apps", path=path)
        cluster = get_cluster("staging", path)
        assert cluster is not None
        assert cluster.name == "staging"
        assert cluster.context == "staging-gke"
        assert cluster.namespace == "staging-apps"

    def test_get_nonexistent(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        assert get_cluster("nope", path) is None


class TestCurrentCluster:
    """Tests for get/set current cluster."""

    def test_no_current_initially(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        assert get_current_cluster_name(path) is None

    def test_set_current(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        add_cluster("staging", "staging-gke", path=path)
        add_cluster("prod", "prod-eks", path=path)
        set_current_cluster("prod", path)
        assert get_current_cluster_name(path) == "prod"

    def test_set_nonexistent_raises(self, tmp_path: pathlib.Path) -> None:
        path = tmp_path / "config.yaml"
        try:
            set_current_cluster("nope", path)
            raise AssertionError("Should have raised ValueError")
        except ValueError:
            pass

    def test_current_returns_none_if_cluster_deleted(self, tmp_path: pathlib.Path) -> None:
        """If current_cluster in file points to a deleted cluster, return None."""
        path = tmp_path / "config.yaml"
        write_config(
            {
                "version": 1,
                "current_cluster": "ghost",
                "clusters": {},
            },
            path,
        )
        assert get_current_cluster_name(path) is None
