import os
import subprocess
import sys
import pytest
from pathlib import Path
from unittest.mock import MagicMock, patch

from brynq import launcher

@pytest.fixture
def launchers_dir(tmp_path):
    with patch("brynq.launcher._DEFAULT_LAUNCHERS_DIR", tmp_path / "launchers"):
        yield tmp_path / "launchers"

class TestCreateClaudeBat:
    def test_creates_file_in_launchers_dir(self, launchers_dir, tmp_path):
        path = launcher.create_claude_bat("testbot", "do task", str(tmp_path))
        assert path.parent == launchers_dir
        assert path.name == "testbot.bat"
        assert path.exists()

    def test_content_includes_claude_command(self, launchers_dir, tmp_path):
        path = launcher.create_claude_bat("testbot", "do task", str(tmp_path))
        content = path.read_text()
        assert "claude --yes --append-system-prompt" in content
        assert "do task" in content

class TestCreateGeminiBat:
    def test_creates_file_in_launchers_dir(self, launchers_dir, tmp_path):
        path = launcher.create_gemini_bat("testbot", "do task", str(tmp_path))
        assert path.parent == launchers_dir
        assert path.name == "testbot.bat"
        assert path.exists()

    def test_content_includes_gemini_command(self, launchers_dir, tmp_path):
        path = launcher.create_gemini_bat("testbot", "do task", str(tmp_path))
        content = path.read_text()
        assert "gemini --yolo -i" in content
        assert "do task" in content

class TestCreateLocalBat:
    def test_creates_file_with_local_suffix(self, launchers_dir, tmp_path):
        path = launcher.create_local_bat("testbot", "ollama", "llama3", "general", str(tmp_path))
        assert path.name == "testbot_local.bat"

    def test_content_invokes_agent_loop(self, launchers_dir, tmp_path):
        path = launcher.create_local_bat("testbot", "ollama", "llama3", "general", str(tmp_path))
        content = path.read_text()
        assert "python -m brynq.agent_loop" in content
        assert '--name "testbot"' in content
        assert '--backend "ollama"' in content

class TestCreateSwarmBats:
    def test_creates_bat_for_each_agent(self, launchers_dir, tmp_path):
        agents = [{"name": "a1", "platform": "claude"}, {"name": "a2", "platform": "local"}]
        paths = launcher.create_swarm_bats("swarm", "task", agents, str(tmp_path))
        assert len(paths) == 2
        assert paths[0].name == "a1.bat"
        assert paths[1].name == "a2_local.bat"

    def test_content_includes_swarm_preamble(self, launchers_dir, tmp_path):
        agents = [{"name": "a1", "platform": "claude"}]
        paths = launcher.create_swarm_bats("swarm", "task", agents, str(tmp_path))
        content = paths[0].read_text()
        assert "part of swarm 'swarm'" in content

class TestLaunchBat:
    @patch("subprocess.Popen")
    def test_invokes_cmd_start_on_windows(self, mock_popen, tmp_path):
        bat = tmp_path / "test.bat"
        bat.write_text("echo hi")
        launcher.launch_bat(bat)
        mock_popen.assert_called_once()
        cmd_args = mock_popen.call_args[0][0]
        assert cmd_args[0] == "cmd"
        assert cmd_args[1] == "/c"
        assert cmd_args[2] == "start"

    def test_raises_file_not_found_if_missing(self):
        with pytest.raises(FileNotFoundError):
            launcher.launch_bat(Path("does_not_exist.bat"))

class TestSafeBatString:
    def test_escapes_quotes_newlines_percents(self):
        s = "Hello\nWorld \"quotes\" 100%"
        res = launcher._safe_bat_string(s)
        assert "\n" not in res
        assert '"' not in res
        assert "%%" in res
        assert "'" in res

class TestInternalHelpers:
    @patch("pathlib.Path.exists", return_value=True)
    @patch("pathlib.Path.read_text", return_value="Here is some mock content without newlines.")
    def test_load_project_context_readme(self, mock_read, mock_exists, tmp_path):
        import brynq.launcher as l
        res = l._load_project_context(str(tmp_path))
        assert "mock content" in res

    @patch("brynq.launcher._DEFAULT_LAUNCHERS_DIR")
    def test_launchers_dir_env_var(self, mock_def, tmp_path):
        import os
        with patch.dict(os.environ, {"BRYNQ_LAUNCHERS_DIR": str(tmp_path)}):
            import brynq.launcher as l
            assert l._launchers_dir() == tmp_path

    @patch("pathlib.Path.exists", return_value=True)
    @patch("pathlib.Path.read_text", return_value="description = 'Test Proj'")
    def test_load_project_context_pyproject(self, mock_read, mock_exists, tmp_path):
        import brynq.launcher as l
        res = l._load_project_context(str(tmp_path))
        assert "Test Proj" in res
