"""Tests for the CLI commands."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

from click.testing import CliRunner

from redhat_oracle.cli.app import main


class TestMainGroup:
    def test_shows_help_without_subcommand(self) -> None:
        runner = CliRunner()
        with patch("redhat_oracle.cli.app.load_config") as mock_config:
            mock_config.return_value = MagicMock()
            result = runner.invoke(main, [])
        assert result.exit_code == 0
        assert "Red Hat Oracle" in result.output

    def test_version_flag(self) -> None:
        runner = CliRunner()
        result = runner.invoke(main, ["--version"])
        assert result.exit_code == 0
        assert "oracle" in result.output


class TestStatusCommand:
    def test_status_shows_collections(self) -> None:
        runner = CliRunner()
        mock_store = MagicMock()
        mock_store.collection_stats.return_value = {
            "oracle_code": 100,
            "oracle_jira": 50,
            "oracle_slack": 25,
            "oracle_web": 10,
            "oracle_pages": 5,
        }

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.vectordb.store.VectorStore", return_value=mock_store),
        ):
            mock_config.return_value = MagicMock()
            result = runner.invoke(main, ["status"])

        assert result.exit_code == 0
        assert "100" in result.output
        assert "50" in result.output
        assert "25" in result.output


class TestSearchCommand:
    def test_search_no_results(self) -> None:
        runner = CliRunner()
        mock_store = MagicMock()
        mock_store.search.return_value = []

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.vectordb.store.VectorStore", return_value=mock_store),
        ):
            mock_config.return_value = MagicMock()
            result = runner.invoke(main, ["search", "test query"])

        assert result.exit_code == 0
        assert "No results found" in result.output

    def test_search_with_results(self) -> None:
        runner = CliRunner()
        mock_store = MagicMock()
        mock_store.search.return_value = [
            {
                "id": "chunk1",
                "text": "def hello(): print('hello world')",
                "metadata": {
                    "source": "github",
                    "org": "myorg",
                    "repo": "myrepo",
                    "path": "main.py",
                },
                "score": 0.85,
            }
        ]

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.vectordb.store.VectorStore", return_value=mock_store),
        ):
            mock_config.return_value = MagicMock()
            result = runner.invoke(main, ["search", "hello function"])

        assert result.exit_code == 0
        assert "0.85" in result.output

    def test_search_with_source_filter(self) -> None:
        runner = CliRunner()
        mock_store = MagicMock()
        mock_store.search.return_value = []

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.vectordb.store.VectorStore", return_value=mock_store),
        ):
            mock_config.return_value = MagicMock()
            result = runner.invoke(main, ["search", "test", "--source", "github"])

        assert result.exit_code == 0
        mock_store.search.assert_called_once_with("test", source="github", top_k=10)


class TestConfigCommand:
    def test_config_shows_yaml(self) -> None:
        runner = CliRunner()

        with patch("redhat_oracle.cli.app.load_config") as mock_config:
            from redhat_oracle.core.config import OracleConfig

            mock_config.return_value = OracleConfig()
            result = runner.invoke(main, ["config"])

        assert result.exit_code == 0
        assert "ollama" in result.output

    def test_config_masks_secrets(self) -> None:
        runner = CliRunner()

        with patch("redhat_oracle.cli.app.load_config") as mock_config:
            from redhat_oracle.core.config import OracleConfig

            config = OracleConfig(
                sources={"github": {"token": "secret123"}}  # type: ignore[arg-type]
            )
            mock_config.return_value = config
            result = runner.invoke(main, ["config"])

        assert result.exit_code == 0
        assert "secret123" not in result.output
        assert "***" in result.output


class TestIngestCommand:
    def test_ingest_handles_no_sources(self, tmp_path: Path) -> None:
        runner = CliRunner()

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.vectordb.store.VectorStore") as mock_store_cls,
        ):
            from redhat_oracle.core.config import OracleConfig

            mock_config.return_value = OracleConfig(data_dir=str(tmp_path))
            mock_store = MagicMock()
            mock_store.collection_stats.return_value = {}
            mock_store_cls.return_value = mock_store

            # Ingest will try to sync with no tokens configured
            # It should complete without crashing
            result = runner.invoke(main, ["ingest", "--source", "slack"])

        # Should not crash — Slack sync returns empty when no token
        assert result.exit_code == 0


class TestChatCommand:
    def test_chat_resume_no_session(self) -> None:
        runner = CliRunner()

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.session.load_latest_session", return_value=None),
            patch("redhat_oracle.core.llm.ollama.OllamaLLM") as mock_llm_cls,
            patch("redhat_oracle.core.vectordb.store.VectorStore"),
        ):
            from redhat_oracle.core.config import OracleConfig

            mock_config.return_value = OracleConfig()
            mock_llm = MagicMock()
            mock_llm._check_available = AsyncMock()
            mock_llm_cls.return_value = mock_llm

            # Simulate user typing "exit" immediately
            result = runner.invoke(main, ["chat", "--resume"], input="exit\n")

        assert result.exit_code == 0
        assert "session" in result.output.lower()

    def test_chat_session_not_found(self) -> None:
        runner = CliRunner()

        with (
            patch("redhat_oracle.cli.app.load_config") as mock_config,
            patch("redhat_oracle.core.session.load_session", return_value=None),
        ):
            from redhat_oracle.core.config import OracleConfig

            mock_config.return_value = OracleConfig()
            result = runner.invoke(main, ["chat", "--session", "nonexistent"])

        assert result.exit_code == 1
        assert "not found" in result.output


class TestRunAsync:
    def test_run_async_returns_result(self) -> None:
        from redhat_oracle.cli.app import _run_async

        async def coro() -> int:
            return 42

        assert _run_async(coro()) == 42

    def test_run_async_propagates_exception(self) -> None:
        import pytest

        from redhat_oracle.cli.app import _run_async

        async def failing() -> None:
            raise ValueError("test error")

        with pytest.raises(ValueError, match="test error"):
            _run_async(failing())
