"""Tests for the AgentDuel CLI."""

import unittest
from unittest.mock import MagicMock, patch
from click.testing import CliRunner

from agentduel_cli.main import cli, VALID_GAMES


class TestTrainCommand(unittest.TestCase):
    """Test cases for the train command."""

    def setUp(self):
        """Set up test fixtures."""
        self.runner = CliRunner()

    def test_train_requires_game_or_all_or_agent_property(self):
        """Test that train command requires --game, --all, or agent GAME property."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value="/fake/agent.py"):
                with patch("os.path.exists", return_value=True):
                    with patch("agentduel_cli.main.get_agent_game", return_value=None):
                        with patch("agentduel_cli.main.load_agent"):
                            result = self.runner.invoke(cli, ["train"])

        self.assertNotEqual(result.exit_code, 0)
        self.assertIn("must specify a game type", result.output.lower())

    def test_train_with_game_flag(self):
        """Test train command with --game flag."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value=None):
                result = self.runner.invoke(cli, ["train", "--game", "split-or-steal"])

        # Should fail because no agent set, but not because of missing --game
        self.assertIn("No active agent set", result.output)

    def test_train_with_short_game_flag(self):
        """Test train command with -g short flag."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value=None):
                result = self.runner.invoke(cli, ["train", "-g", "liars-dice"])

        # Should fail because no agent set, but not because of missing --game
        self.assertIn("No active agent set", result.output)

    def test_train_with_all_flag(self):
        """Test train command with --all flag."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value=None):
                result = self.runner.invoke(cli, ["train", "--all"])

        # Should fail because no agent set, but not because of missing flags
        self.assertIn("No active agent set", result.output)

    def test_train_invalid_game_type(self):
        """Test train command with invalid game type."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            result = self.runner.invoke(cli, ["train", "--game", "invalid-game"])

        self.assertNotEqual(result.exit_code, 0)
        self.assertIn("Invalid value", result.output)

    def test_train_requires_login(self):
        """Test that train command requires login."""
        with patch("agentduel_cli.main.get_token", return_value=None):
            result = self.runner.invoke(cli, ["train", "--game", "split-or-steal"])

        self.assertIn("Not logged in", result.output)


class TestChallengeCommand(unittest.TestCase):
    """Test cases for the challenge command."""

    def setUp(self):
        """Set up test fixtures."""
        self.runner = CliRunner()

    def test_challenge_requires_game_or_agent_property(self):
        """Test that challenge command requires --game or agent GAME property."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value="/fake/agent.py"):
                with patch("os.path.exists", return_value=True):
                    with patch("agentduel_cli.main.get_agent_game", return_value=None):
                        with patch("agentduel_cli.main.load_agent"):
                            result = self.runner.invoke(cli, ["challenge"])

        self.assertNotEqual(result.exit_code, 0)
        self.assertIn("must specify a game type", result.output.lower())

    def test_challenge_with_game_flag(self):
        """Test challenge command with --game flag."""
        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.main.get_active_agent", return_value=None):
                result = self.runner.invoke(cli, ["challenge", "--game", "liars-dice"])

        # Should fail because no agent set, but not because of missing --game
        self.assertIn("No active agent set", result.output)

    def test_challenge_requires_login(self):
        """Test that challenge command requires login."""
        with patch("agentduel_cli.main.get_token", return_value=None):
            result = self.runner.invoke(cli, ["challenge", "--game", "split-or-steal"])

        self.assertIn("Not logged in", result.output)


class TestListCommand(unittest.TestCase):
    """Test cases for the list command."""

    def setUp(self):
        """Set up test fixtures."""
        self.runner = CliRunner()

    def test_list_shows_game_type_column(self):
        """Test that list command shows game type column."""
        mock_challenges = {
            "results": [
                {
                    "id": 1,
                    "agent_name": "TestAgent",
                    "game_type": "split-or-steal",
                    "created_at": "2024-01-01T00:00:00Z",
                    "expires_at": "2024-01-01T00:05:00Z",
                },
                {
                    "id": 2,
                    "agent_name": "TestAgent2",
                    "game_type": "liars-dice",
                    "created_at": "2024-01-01T00:00:00Z",
                    "expires_at": "2024-01-01T00:05:00Z",
                },
            ]
        }

        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.api.APIClient.list_challenges", return_value=mock_challenges):
                result = self.runner.invoke(cli, ["list"])

        # Should show game type column
        self.assertIn("Game", result.output)
        self.assertIn("Split or Steal", result.output)
        self.assertIn("Liar's Dice", result.output)


class TestMatchesCommand(unittest.TestCase):
    """Test cases for the matches command."""

    def setUp(self):
        """Set up test fixtures."""
        self.runner = CliRunner()

    def test_matches_shows_game_type_column(self):
        """Test that matches command shows game type column."""
        mock_matches = {
            "results": [
                {
                    "id": 1,
                    "agent_b_name": "TrainingBot",
                    "game_type": "split-or-steal",
                    "score_a": 30,
                    "score_b": 20,
                    "is_draw": False,
                    "created_at": "2024-01-01T00:00:00Z",
                },
                {
                    "id": 2,
                    "agent_b_name": "TrainingBot",
                    "game_type": "liars-dice",
                    "score_a": 15,
                    "score_b": 10,
                    "is_draw": False,
                    "created_at": "2024-01-01T00:00:00Z",
                },
            ]
        }

        with patch("agentduel_cli.main.get_token", return_value="test_token"):
            with patch("agentduel_cli.api.APIClient.get_my_matches", return_value=mock_matches):
                result = self.runner.invoke(cli, ["matches"])

        # Should show game type column
        self.assertIn("Game", result.output)


class TestValidGames(unittest.TestCase):
    """Test that VALID_GAMES is correct."""

    def test_valid_games_list(self):
        """Test that VALID_GAMES contains expected games."""
        self.assertIn("split-or-steal", VALID_GAMES)
        self.assertIn("liars-dice", VALID_GAMES)
        self.assertEqual(len(VALID_GAMES), 2)


class TestGameRouting(unittest.TestCase):
    """Test that game type is correctly passed through."""

    def setUp(self):
        """Set up test fixtures."""
        self.runner = CliRunner()

    @patch("agentduel_cli.main.play_match")
    @patch("agentduel_cli.main.load_agent")
    @patch("agentduel_cli.main.APIClient")
    @patch("agentduel_cli.main.get_token", return_value="test_token")
    @patch("agentduel_cli.main.get_active_agent", return_value="/fake/agent.py")
    @patch("os.path.exists", return_value=True)
    def test_train_passes_game_id_split_or_steal(
        self, mock_exists, mock_active_agent, mock_token, mock_api, mock_load, mock_play
    ):
        """Test that train command passes split-or-steal game_id."""
        mock_load.return_value = MagicMock()
        mock_api_instance = MagicMock()
        mock_api.return_value = mock_api_instance

        self.runner.invoke(cli, ["train", "--game", "split-or-steal"])

        mock_play.assert_called_once()
        call_kwargs = mock_play.call_args
        self.assertEqual(call_kwargs.kwargs.get("game_id"), "split-or-steal")

    @patch("agentduel_cli.main.play_match")
    @patch("agentduel_cli.main.load_agent")
    @patch("agentduel_cli.main.APIClient")
    @patch("agentduel_cli.main.get_token", return_value="test_token")
    @patch("agentduel_cli.main.get_active_agent", return_value="/fake/agent.py")
    @patch("os.path.exists", return_value=True)
    def test_train_passes_game_id_liars_dice(
        self, mock_exists, mock_active_agent, mock_token, mock_api, mock_load, mock_play
    ):
        """Test that train command passes liars-dice game_id."""
        mock_load.return_value = MagicMock()
        mock_api_instance = MagicMock()
        mock_api.return_value = mock_api_instance

        self.runner.invoke(cli, ["train", "--game", "liars-dice"])

        mock_play.assert_called_once()
        call_kwargs = mock_play.call_args
        self.assertEqual(call_kwargs.kwargs.get("game_id"), "liars-dice")

    @patch("agentduel_cli.main._run_single_training_match")
    @patch("agentduel_cli.main.load_agent")
    @patch("agentduel_cli.main.get_token", return_value="test_token")
    @patch("agentduel_cli.main.get_active_agent", return_value="/fake/agent.py")
    @patch("os.path.exists", return_value=True)
    def test_train_all_plays_both_games(
        self, mock_exists, mock_active_agent, mock_token, mock_load, mock_run
    ):
        """Test that --all flag plays both games."""
        mock_load.return_value = MagicMock()
        mock_run.return_value = True

        self.runner.invoke(cli, ["train", "--all"])

        # Should be called twice, once for each game
        self.assertEqual(mock_run.call_count, 2)

        # Get the game types passed
        game_types = [call.args[2] for call in mock_run.call_args_list]
        self.assertIn("split-or-steal", game_types)
        self.assertIn("liars-dice", game_types)


if __name__ == "__main__":
    unittest.main()
