"""Tests for the first-run experience."""

import json
import os
from argparse import Namespace
from unittest.mock import patch

from codeflash.setup.first_run import (
    _handle_api_key,
    _prompt_confirmation,
    _show_detected_settings,
    _show_welcome,
    handle_first_run,
    is_first_run,
)


class TestIsFirstRun:
    """Tests for is_first_run function."""

    def test_returns_true_when_no_config(self, tmp_path):
        """Should return True when no codeflash config exists."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')

        result = is_first_run(tmp_path)
        assert result is True

    def test_returns_false_when_pyproject_config_exists(self, tmp_path):
        """Should return False when codeflash config exists in pyproject.toml."""
        (tmp_path / "pyproject.toml").write_text('[tool.codeflash]\nmodule-root = "src"')

        result = is_first_run(tmp_path)
        assert result is False

    def test_returns_false_when_package_json_config_exists(self, tmp_path):
        """Should return False when codeflash config exists in package.json."""
        (tmp_path / "package.json").write_text(json.dumps({"name": "test", "codeflash": {"moduleRoot": "src"}}))

        result = is_first_run(tmp_path)
        assert result is False

    def test_returns_true_for_empty_directory(self, tmp_path):
        """Should return True for empty directory."""
        result = is_first_run(tmp_path)
        assert result is True


class TestHandleFirstRun:
    """Tests for handle_first_run function."""

    def test_returns_args_on_success(self, tmp_path, monkeypatch):
        """Should return updated args on successful first run."""
        # Create Python project
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        (tmp_path / "src").mkdir()
        (tmp_path / "src" / "__init__.py").write_text("")
        (tmp_path / "tests").mkdir()

        # Mock user input and API key
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-test-key")

        # Skip confirmation
        result = handle_first_run(skip_confirm=True, skip_api_key=True)

        assert result is not None
        assert hasattr(result, "module_root")
        assert hasattr(result, "language")
        assert result.language == "python"

    def test_returns_none_when_user_cancels(self, tmp_path, monkeypatch):
        """Should return None when user cancels."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        monkeypatch.chdir(tmp_path)

        # Mock user cancellation
        with patch("codeflash.setup.first_run._prompt_confirmation", return_value="n"):
            result = handle_first_run(skip_api_key=True)

        assert result is None

    def test_skips_confirm_with_flag(self, tmp_path, monkeypatch):
        """Should skip confirmation when skip_confirm=True."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        (tmp_path / "src").mkdir()
        (tmp_path / "src" / "__init__.py").write_text("")
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-test-key")

        # Should not prompt for confirmation
        with patch("codeflash.setup.first_run._prompt_confirmation") as mock_prompt:
            result = handle_first_run(skip_confirm=True, skip_api_key=True)

        mock_prompt.assert_not_called()
        assert result is not None

    def test_merges_with_existing_args(self, tmp_path, monkeypatch):
        """Should merge detected settings with existing args."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        (tmp_path / "src").mkdir()
        (tmp_path / "src" / "__init__.py").write_text("")
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-test-key")

        existing_args = Namespace(custom_flag=True, module_root=None)

        result = handle_first_run(args=existing_args, skip_confirm=True, skip_api_key=True)

        assert result is not None
        assert result.custom_flag is True  # Preserved
        assert result.module_root is not None  # Updated


class TestPromptConfirmation:
    """Tests for _prompt_confirmation function."""

    def test_returns_y_for_yes(self, monkeypatch):
        """Should return 'y' for yes input."""
        monkeypatch.setattr("sys.stdin.isatty", lambda: True)

        with patch("codeflash.cli_cmds.console.console.input", return_value="y"):
            result = _prompt_confirmation()

        assert result == "y"

    def test_returns_y_for_empty(self, monkeypatch):
        """Should return 'y' for empty input (default)."""
        monkeypatch.setattr("sys.stdin.isatty", lambda: True)

        with patch("codeflash.cli_cmds.console.console.input", return_value=""):
            result = _prompt_confirmation()

        assert result == "y"

    def test_returns_n_for_no(self, monkeypatch):
        """Should return 'n' for no input."""
        monkeypatch.setattr("sys.stdin.isatty", lambda: True)

        with patch("codeflash.cli_cmds.console.console.input", return_value="n"):
            result = _prompt_confirmation()

        assert result == "n"

    def test_returns_customize_for_c(self, monkeypatch):
        """Should return 'customize' for c input."""
        monkeypatch.setattr("sys.stdin.isatty", lambda: True)

        with patch("codeflash.cli_cmds.console.console.input", return_value="c"):
            result = _prompt_confirmation()

        assert result == "customize"

    def test_returns_n_for_non_interactive(self, monkeypatch):
        """Should return 'n' for non-interactive environment."""
        monkeypatch.setattr("sys.stdin.isatty", lambda: False)

        result = _prompt_confirmation()

        assert result == "n"


class TestHandleApiKey:
    """Tests for _handle_api_key function."""

    def test_returns_true_when_key_exists(self, monkeypatch):
        """Should return True when API key already exists."""
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-existing-key")

        with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", return_value="cf-existing-key"):
            result = _handle_api_key()

        assert result is True

    def test_accepts_valid_key(self, monkeypatch):
        """Should accept valid API key starting with cf-."""
        monkeypatch.delenv("CODEFLASH_API_KEY", raising=False)

        with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", side_effect=OSError):
            with patch("codeflash.cli_cmds.console.console.input", return_value="cf-valid-key"):
                # Patch the entire import to fail, triggering the exception handler
                with patch.dict("sys.modules", {"codeflash.code_utils.shell_utils": None}):
                    result = _handle_api_key()

        assert result is True
        assert os.environ.get("CODEFLASH_API_KEY") == "cf-valid-key"

    def test_rejects_invalid_key(self, monkeypatch):
        """Should reject API key not starting with cf-."""
        monkeypatch.delenv("CODEFLASH_API_KEY", raising=False)

        with patch("codeflash.code_utils.env_utils.get_codeflash_api_key", side_effect=OSError):
            with patch("codeflash.cli_cmds.console.console.input", return_value="invalid-key"):
                result = _handle_api_key()

        assert result is False


class TestShowFunctions:
    """Tests for display functions (smoke tests)."""

    def test_show_welcome_does_not_crash(self):
        """Should not crash when showing welcome message."""
        # Just verify it doesn't raise an exception
        _show_welcome()

    def test_show_detected_settings_does_not_crash(self, tmp_path):
        """Should not crash when showing detected settings."""
        from codeflash.setup.detector import detect_project

        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        detected = detect_project(tmp_path)

        # Just verify it doesn't raise an exception
        _show_detected_settings(detected)


class TestFirstRunIntegration:
    """Integration tests for the complete first-run flow."""

    def test_full_python_first_run(self, tmp_path, monkeypatch):
        """Should complete full first-run for Python project."""
        # Create Python project
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "myapp"\n\n[tool.ruff]\nline-length = 120')
        pkg_dir = tmp_path / "myapp"
        pkg_dir.mkdir()
        (pkg_dir / "__init__.py").write_text("")
        (tmp_path / "tests").mkdir()

        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-test-key")

        result = handle_first_run(skip_confirm=True, skip_api_key=True)

        assert result is not None
        assert result.language == "python"
        assert result.module_root.endswith("myapp")
        assert result.tests_root.endswith("tests")

        # Verify config was written
        import tomlkit

        content = (tmp_path / "pyproject.toml").read_text()
        data = tomlkit.parse(content)
        assert "codeflash" in data["tool"]

    def test_full_javascript_first_run(self, tmp_path, monkeypatch):
        """Should complete full first-run for JavaScript project."""
        # Create JS project
        (tmp_path / "package.json").write_text(
            json.dumps({"name": "myapp", "devDependencies": {"jest": "^29.0.0"}}, indent=2)
        )
        (tmp_path / "src").mkdir()
        (tmp_path / "tests").mkdir()

        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("CODEFLASH_API_KEY", "cf-test-key")

        result = handle_first_run(skip_confirm=True, skip_api_key=True)

        assert result is not None
        assert result.language == "javascript"
        assert result.module_root.endswith("src")
        assert result.pytest_cmd == "jest"  # test_runner mapped to pytest_cmd

    def test_subsequent_run_uses_saved_config(self, tmp_path, monkeypatch):
        """After first run, subsequent runs should not trigger first-run."""
        # Create project with existing config
        (tmp_path / "pyproject.toml").write_text('[tool.codeflash]\nmodule-root = "src"')

        monkeypatch.chdir(tmp_path)

        # Should not be first run
        assert is_first_run(tmp_path) is False
