"""Tests for gaol.devcontainer.cli_helper."""

from __future__ import annotations

import json

from gaol.devcontainer.cli_helper import (
    features_to_overlays,
    flatten_lifecycle_command,
    get_overlays,
    get_parsed_state,
    image_to_overlays,
)
from gaol.devcontainer.models import DevcontainerSpec


class TestImageToOverlays:
    """Tests for image_to_overlays()."""

    def test_none_image(self) -> None:
        assert image_to_overlays(None) == []

    def test_empty_image(self) -> None:
        assert image_to_overlays("") == []

    def test_simple_python(self) -> None:
        assert image_to_overlays("python:3.12") == ["python"]

    def test_mcr_python(self) -> None:
        assert image_to_overlays("mcr.microsoft.com/devcontainers/python:3.12") == ["python"]

    def test_simple_node(self) -> None:
        assert image_to_overlays("node:20") == ["node"]

    def test_mcr_javascript_node(self) -> None:
        result = image_to_overlays("mcr.microsoft.com/devcontainers/javascript-node:20")
        assert "node" in result

    def test_mcr_typescript_node(self) -> None:
        result = image_to_overlays("mcr.microsoft.com/devcontainers/typescript-node:20")
        assert "node" in result

    def test_simple_rust(self) -> None:
        assert image_to_overlays("rust:1.75") == ["rust"]

    def test_mcr_rust(self) -> None:
        assert image_to_overlays("mcr.microsoft.com/devcontainers/rust:1") == ["rust"]

    def test_simple_go(self) -> None:
        assert image_to_overlays("golang:1.22") == ["go"]

    def test_mcr_go(self) -> None:
        assert image_to_overlays("mcr.microsoft.com/devcontainers/go:1.22") == ["go"]

    def test_unknown_image(self) -> None:
        assert image_to_overlays("nginx:latest") == []

    def test_no_tag(self) -> None:
        assert image_to_overlays("python") == ["python"]

    def test_no_duplicates(self) -> None:
        # "python" matches only once
        result = image_to_overlays("python:3.12")
        assert result == ["python"]


class TestFeaturesToOverlays:
    """Tests for features_to_overlays()."""

    def test_empty_features(self) -> None:
        assert features_to_overlays({}) == []

    def test_python_feature(self) -> None:
        features = {"ghcr.io/devcontainers/features/python:1": {"version": "3.12"}}
        assert features_to_overlays(features) == ["python"]

    def test_node_feature(self) -> None:
        features = {"ghcr.io/devcontainers/features/node:1": {}}
        assert features_to_overlays(features) == ["node"]

    def test_rust_feature(self) -> None:
        features = {"ghcr.io/devcontainers/features/rust:1": {}}
        assert features_to_overlays(features) == ["rust"]

    def test_go_feature(self) -> None:
        features = {"ghcr.io/devcontainers/features/go:1": {}}
        assert features_to_overlays(features) == ["go"]

    def test_multiple_features(self) -> None:
        features = {
            "ghcr.io/devcontainers/features/python:1": {},
            "ghcr.io/devcontainers/features/node:1": {},
        }
        result = features_to_overlays(features)
        assert "python" in result
        assert "node" in result

    def test_unknown_feature(self) -> None:
        features = {"ghcr.io/devcontainers/features/docker-in-docker:2": {}}
        assert features_to_overlays(features) == []

    def test_no_duplicates(self) -> None:
        features = {
            "ghcr.io/devcontainers/features/python:1": {},
            "ghcr.io/devcontainers/features/python:2": {},
        }
        result = features_to_overlays(features)
        assert result == ["python"]


class TestFlattenLifecycleCommand:
    """Tests for flatten_lifecycle_command()."""

    def test_none(self) -> None:
        assert flatten_lifecycle_command(None) is None

    def test_empty_string(self) -> None:
        assert flatten_lifecycle_command("") is None

    def test_whitespace_string(self) -> None:
        assert flatten_lifecycle_command("   ") is None

    def test_simple_string(self) -> None:
        assert flatten_lifecycle_command("npm install") == "npm install"

    def test_list_args(self) -> None:
        assert flatten_lifecycle_command(["npm", "install"]) == "npm install"

    def test_empty_list(self) -> None:
        assert flatten_lifecycle_command([]) is None

    def test_dict_single(self) -> None:
        result = flatten_lifecycle_command({"install": "npm install"})
        assert result == "npm install"

    def test_dict_multiple(self) -> None:
        result = flatten_lifecycle_command({
            "install": "npm install",
            "build": "npm run build",
        })
        assert result == "npm install && npm run build"

    def test_dict_with_list_value(self) -> None:
        result = flatten_lifecycle_command({"install": ["pip", "install", "-r", "requirements.txt"]})
        assert result == "pip install -r requirements.txt"

    def test_empty_dict(self) -> None:
        assert flatten_lifecycle_command({}) is None

    def test_dict_with_empty_values(self) -> None:
        result = flatten_lifecycle_command({"a": "", "b": "echo hello"})
        assert result == "echo hello"


class TestGetOverlays:
    """Tests for get_overlays() combining image and feature detection."""

    def test_image_only(self) -> None:
        spec = DevcontainerSpec(image="python:3.12")
        assert get_overlays(spec) == ["python"]

    def test_features_only(self) -> None:
        spec = DevcontainerSpec(
            features={"ghcr.io/devcontainers/features/node:1": {}},
        )
        assert get_overlays(spec) == ["node"]

    def test_image_and_features_dedup(self) -> None:
        spec = DevcontainerSpec(
            image="python:3.12",
            features={"ghcr.io/devcontainers/features/python:1": {}},
        )
        result = get_overlays(spec)
        assert result == ["python"]

    def test_image_plus_different_feature(self) -> None:
        spec = DevcontainerSpec(
            image="python:3.12",
            features={"ghcr.io/devcontainers/features/node:1": {}},
        )
        result = get_overlays(spec)
        assert result == ["python", "node"]


class TestGetParsedState:
    """Tests for get_parsed_state()."""

    def test_minimal_spec(self) -> None:
        spec = DevcontainerSpec(image="python:3.12")
        state = get_parsed_state(spec)
        assert state["env"] == {}
        assert state["onCreateCommand"] is None
        assert state["postCreateCommand"] is None
        assert state["postStartCommand"] is None
        assert state["features"] == {}
        assert state["forwardPorts"] == []

    def test_full_spec(self) -> None:
        spec = DevcontainerSpec(
            image="python:3.12",
            container_env={"FOO": "bar"},
            remote_env={"BAZ": "qux"},
            on_create_command="pip install -e .",
            post_create_command={"setup": "make setup", "test": "make test"},
            post_start_command=["python", "-m", "myserver"],
            features={"ghcr.io/devcontainers/features/node:1": {"version": "20"}},
            forward_ports=[3000, 8080],
            remote_user="vscode",
            workspace_folder="/workspace",
        )
        state = get_parsed_state(spec)
        assert state["env"] == {"FOO": "bar", "BAZ": "qux"}
        assert state["onCreateCommand"] == "pip install -e ."
        assert state["postCreateCommand"] == "make setup && make test"
        assert state["postStartCommand"] == "python -m myserver"
        assert state["features"] == {
            "ghcr.io/devcontainers/features/node:1": {"version": "20"}
        }
        assert state["forwardPorts"] == [3000, 8080]
        assert state["remoteUser"] == "vscode"
        assert state["workspaceFolder"] == "/workspace"

    def test_state_is_json_serializable(self) -> None:
        spec = DevcontainerSpec(
            image="python:3.12",
            container_env={"KEY": "value"},
            post_create_command="echo hello",
        )
        state = get_parsed_state(spec)
        # Should not raise
        result = json.dumps(state)
        assert isinstance(result, str)
