"""Tests for ``ilum cluster create`` interactive prompts."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest
from typer.testing import CliRunner

from ilum.cli.main import app


@pytest.fixture()
def runner() -> CliRunner:
    return CliRunner()


@pytest.fixture()
def _mock_cluster_create():
    """Patch ClusterManager.create to avoid real subprocess calls."""
    from ilum.config.models import ClusterRecord

    record = ClusterRecord(
        name="ilum-dev",
        provider="k3d",
        kubecontext="k3d-ilum-dev",
        created_at="2026-01-01T00:00:00",
    )
    with (
        patch("ilum.cli.cluster_cmd.ClusterManager") as mock_cls,
        patch("ilum.cli.cluster_cmd.ConfigManager") as mock_cfg_cls,
        patch("ilum.cli.cluster_cmd.IlumPaths"),
    ):
        mock_cls.return_value.create.return_value = record
        mock_cfg_cls.return_value.ensure_config.return_value = MagicMock(clusters=[])
        yield mock_cls


def _patch_interactive(value: bool = True):
    """Patch _is_interactive in the cluster_cmd module."""
    return patch("ilum.cli.cluster_cmd._is_interactive", return_value=value)


class TestCreateInteractiveProvider:
    """Provider prompt when --provider is omitted."""

    def test_no_provider_flag_prompts(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When --provider is omitted, questionary.select is called."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(True),
        ):
            mock_q.select.return_value.ask.return_value = "kind"
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(app, ["cluster", "create", "--preset", "dev"])

        mock_q.select.assert_any_call(
            "Cluster provider:",
            choices=["k3d", "minikube", "kind"],
        )
        assert result.exit_code == 0

    def test_explicit_provider_skips_prompt(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When --provider is given, no prompt is shown."""
        with patch("ilum.cli.cluster_cmd.questionary") as mock_q:
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(
                app, ["cluster", "create", "--provider", "kind", "--preset", "dev"]
            )

        # questionary.select should not have been called for the provider
        for call in mock_q.select.call_args_list:
            assert call[0][0] != "Cluster provider:"
        assert result.exit_code == 0

    def test_ctrl_c_during_provider_prompt(self, runner: CliRunner, _mock_cluster_create) -> None:
        """Ctrl+C (questionary returns None) exits with code 130."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(True),
        ):
            mock_q.select.return_value.ask.return_value = None
            result = runner.invoke(app, ["cluster", "create", "--preset", "dev"])

        assert result.exit_code == 130

    def test_non_interactive_defaults_to_k3d(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When stdin is not a tty, defaults to k3d without prompting."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(False),
        ):
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(app, ["cluster", "create", "--preset", "dev"])

        mock_q.select.assert_not_called()
        assert result.exit_code == 0


class TestCreateInteractivePreset:
    """Preset prompt when --preset is omitted."""

    def test_no_preset_flag_prompts(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When --preset is omitted, questionary.select is called for preset."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(True),
        ):
            mock_q.select.return_value.ask.return_value = "dev"
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(app, ["cluster", "create", "--provider", "k3d"])

        # Should have prompted for preset
        preset_calls = [c for c in mock_q.select.call_args_list if c[0][0] == "Resource preset:"]
        assert len(preset_calls) == 1
        assert result.exit_code == 0

    def test_explicit_preset_skips_prompt(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When --preset is given, no prompt is shown for preset."""
        with patch("ilum.cli.cluster_cmd.questionary") as mock_q:
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(
                app, ["cluster", "create", "--provider", "k3d", "--preset", "full"]
            )

        for call in mock_q.select.call_args_list:
            assert call[0][0] != "Resource preset:"
        assert result.exit_code == 0

    def test_ctrl_c_during_preset_prompt(self, runner: CliRunner, _mock_cluster_create) -> None:
        """Ctrl+C during preset prompt exits with code 130."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(True),
        ):
            # First call (provider) succeeds, second call (preset) returns None
            mock_q.select.return_value.ask.side_effect = ["k3d", None]
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(app, ["cluster", "create"])

        assert result.exit_code == 130

    def test_non_interactive_defaults_to_dev(self, runner: CliRunner, _mock_cluster_create) -> None:
        """When stdin is not a tty, defaults to dev without prompting."""
        with (
            patch("ilum.cli.cluster_cmd.questionary") as mock_q,
            _patch_interactive(False),
        ):
            mock_q.Choice = __import__("questionary").Choice
            result = runner.invoke(app, ["cluster", "create"])

        mock_q.select.assert_not_called()
        assert result.exit_code == 0
