"""
Unit tests for py_agent.cli.main().

Strategy:
- Patch sys.argv to control CLI arguments.
- Patch asyncio.run to avoid actual async execution of _run_generate_project / _run_generate_pr.
- Capture stdout to verify emitted JSON-lines events.
- Use pytest.raises(SystemExit) to assert exit codes.
"""

import json
import sys
from unittest.mock import MagicMock, patch

import pytest

from py_agent.cli import main

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _argv(*extra: str) -> list[str]:
    """Build a sys.argv list starting with the program name."""
    return ["py-agent", *extra]


def _make_asyncio_run_mock(return_value: int = 0) -> MagicMock:
    """Return a mock for asyncio.run that returns *return_value*."""
    return MagicMock(return_value=return_value)


# ---------------------------------------------------------------------------
# 1. Missing API key → error event + exit(1)
# ---------------------------------------------------------------------------


class TestMissingApiKey:
    def test_no_api_key_exits_1(self, capsys, monkeypatch):
        """When no API_KEY env var and no --api-key flag, main() must exit with code 1."""
        monkeypatch.delenv("API_KEY", raising=False)
        with patch.object(sys, "argv", _argv()):
            with pytest.raises(SystemExit) as exc_info:
                main()
        assert exc_info.value.code == 1

    def test_no_api_key_emits_error_event(self, capsys, monkeypatch):
        """When API key is missing, an error JSON event must be written to stdout."""
        monkeypatch.delenv("API_KEY", raising=False)
        with patch.object(sys, "argv", _argv()):
            with pytest.raises(SystemExit):
                main()
        captured = capsys.readouterr()
        event = json.loads(captured.out.strip())
        assert event["type"] == "error"
        assert "API key" in event["message"]


# ---------------------------------------------------------------------------
# 2. API key provided via env var
# ---------------------------------------------------------------------------


class TestApiKeyViaEnv:
    def test_api_key_env_var_proceeds_to_generate_project(self, monkeypatch):
        """API_KEY env var should be picked up and route to generate-project."""
        monkeypatch.setenv("API_KEY", "test-key-from-env")
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv()):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 0
        assert mock_run.call_count == 1


# ---------------------------------------------------------------------------
# 3. API key provided via --api-key flag
# ---------------------------------------------------------------------------


class TestApiKeyViaFlag:
    def test_api_key_flag_proceeds(self, monkeypatch):
        """--api-key flag should satisfy the key check."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "flag-key")):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 0


# ---------------------------------------------------------------------------
# 4. Default command → generate-project
# ---------------------------------------------------------------------------


class TestDefaultCommand:
    def test_no_subcommand_defaults_to_generate_project(self, monkeypatch):
        """When no subcommand is given, main() should call _run_generate_project."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "k")):
            with patch("asyncio.run", mock_run) as patched_run:
                with pytest.raises(SystemExit):
                    main()
        # The coroutine passed to asyncio.run should be from _run_generate_project
        assert patched_run.call_count == 1
        coroutine_arg = patched_run.call_args[0][0]
        # Coroutine name should correspond to _run_generate_project
        assert "generate_project" in coroutine_arg.__qualname__ or "generate_project" in getattr(
            coroutine_arg, "__name__", ""
        )


# ---------------------------------------------------------------------------
# 5. Explicit generate-project command
# ---------------------------------------------------------------------------


class TestGenerateProjectCommand:
    def test_explicit_generate_project_exits_0_on_success(self, monkeypatch):
        """generate-project subcommand with return 0 should exit(0)."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "k", "generate-project")):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 0

    def test_generate_project_exits_1_on_failure(self, monkeypatch):
        """generate-project subcommand with return 1 should exit(1)."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=1)
        with patch.object(sys, "argv", _argv("--api-key", "k", "generate-project")):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 1


# ---------------------------------------------------------------------------
# 6. Explicit generate-pr command
# ---------------------------------------------------------------------------


class TestGeneratePrCommand:
    def test_generate_pr_exits_0_on_success(self, monkeypatch):
        """generate-pr subcommand with return 0 should exit(0)."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "k", "generate-pr")):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 0

    def test_generate_pr_routes_to_run_generate_pr(self, monkeypatch):
        """generate-pr subcommand should call _run_generate_pr coroutine."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "k", "generate-pr")):
            with patch("asyncio.run", mock_run) as patched_run:
                with pytest.raises(SystemExit):
                    main()
        assert patched_run.call_count == 1
        coroutine_arg = patched_run.call_args[0][0]
        assert "generate_pr" in coroutine_arg.__qualname__ or "generate_pr" in getattr(
            coroutine_arg, "__name__", ""
        )

    def test_generate_pr_exits_1_on_failure(self, monkeypatch):
        """generate-pr subcommand with return 1 should exit(1)."""
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=1)
        with patch.object(sys, "argv", _argv("--api-key", "k", "generate-pr")):
            with patch("asyncio.run", mock_run):
                with pytest.raises(SystemExit) as exc_info:
                    main()
        assert exc_info.value.code == 1


# ---------------------------------------------------------------------------
# 7. LOG_LEVEL env var handling
# ---------------------------------------------------------------------------


class TestLogLevel:
    def test_log_level_debug_sets_debug_logging(self, monkeypatch):
        """LOG_LEVEL=DEBUG should configure the root logger at DEBUG level."""
        import logging

        monkeypatch.setenv("LOG_LEVEL", "DEBUG")
        monkeypatch.delenv("API_KEY", raising=False)
        mock_run = _make_asyncio_run_mock(return_value=0)
        with patch.object(sys, "argv", _argv("--api-key", "k")):
            with patch("asyncio.run", mock_run):
                with patch("logging.basicConfig") as mock_basicconfig:
                    with pytest.raises(SystemExit):
                        main()
        mock_basicconfig.assert_called_once()
        call_kwargs = mock_basicconfig.call_args[1]
        assert call_kwargs["level"] == logging.DEBUG
