"""Tests for cli/main.py — error boundary and entry point."""

from __future__ import annotations

from unittest.mock import patch

import click
import pytest
from click.testing import CliRunner

from kuberoku.cli.main import cli, run
from kuberoku.exceptions import AppNotFoundError
from kuberoku.factory import KuberokuFactory
from tests.backends.fake import FakeK8sClient


class TestErrorBoundary:
    def test_kuberoku_error_shows_error(self) -> None:
        runner = CliRunner()
        factory = KuberokuFactory(k8s_client=FakeK8sClient(), namespace="test")
        result = runner.invoke(
            cli,
            ["apps:destroy", "nonexistent", "--confirm", "nonexistent"],
            obj={"factory": factory},
        )
        assert result.exit_code != 0

    def test_run_invokes_cli_help(self) -> None:
        """run() entry point successfully invokes the CLI."""
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.return_value = None
            run()
            mock_cli.assert_called_once()

    def test_cli_creates_factory_if_missing(self) -> None:
        """cli callback creates factory in ctx.obj if not present."""
        runner = CliRunner()
        result = runner.invoke(cli, ["--help"])
        assert result.exit_code == 0

    def test_cli_apps_group_mounted(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["apps", "--help"])
        assert result.exit_code == 0
        assert "Manage apps" in result.output

    def test_cli_link_add_command_mounted(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["apps", "link", "add", "--help"])
        assert result.exit_code == 0
        assert "Link current directory" in result.output

    def test_cli_link_remove_command_mounted(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["apps", "link", "remove", "--help"])
        assert result.exit_code == 0
        assert "Remove .kuberoku" in result.output


class TestRunErrorBoundary:
    """Tests for the run() error boundary paths."""

    def test_run_kuberoku_error_exit_1(self) -> None:
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.side_effect = AppNotFoundError("myapp")
            with pytest.raises(SystemExit, match="1"):
                run()

    def test_run_click_abort_exit_1(self) -> None:
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.side_effect = click.Abort()
            with pytest.raises(SystemExit, match="1"):
                run()

    def test_run_click_exception_shows_message(self) -> None:
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.side_effect = click.ClickException("bad option")
            with pytest.raises(SystemExit):
                run()

    def test_run_unexpected_error_exit_2(self) -> None:
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.side_effect = RuntimeError("boom")
            with pytest.raises(SystemExit, match="2"):
                run()

    def test_run_system_exit_passthrough(self) -> None:
        with patch("kuberoku.cli.main.cli") as mock_cli:
            mock_cli.side_effect = SystemExit(42)
            with pytest.raises(SystemExit, match="42"):
                run()
