import json
import subprocess
import unittest
from unittest.mock import Mock, patch

import httpx
from crewai.cli.triggers.main import TriggersCommand


class TestTriggersCommand(unittest.TestCase):
    @patch("crewai.cli.command.get_auth_token")
    @patch("crewai.cli.command.PlusAPI")
    def setUp(self, mock_plus_api, mock_get_auth_token):
        self.mock_get_auth_token = mock_get_auth_token
        self.mock_plus_api = mock_plus_api

        self.mock_get_auth_token.return_value = "test_token"

        self.triggers_command = TriggersCommand()
        self.mock_client = self.triggers_command.plus_api_client

    @patch("crewai.cli.triggers.main.console.print")
    def test_list_triggers_success(self, mock_console_print):
        mock_response = Mock(spec=httpx.Response)
        mock_response.status_code = 200
        mock_response.ok = True
        mock_response.json.return_value = {
            "apps": [
                {
                    "name": "Test App",
                    "slug": "test-app",
                    "description": "A test application",
                    "is_connected": True,
                    "triggers": [
                        {
                            "name": "Test Trigger",
                            "slug": "test-trigger",
                            "description": "A test trigger"
                        }
                    ]
                }
            ]
        }
        self.mock_client.get_triggers.return_value = mock_response

        self.triggers_command.list_triggers()

        self.mock_client.get_triggers.assert_called_once()
        mock_console_print.assert_any_call("[bold blue]Fetching available triggers...[/bold blue]")

    @patch("crewai.cli.triggers.main.console.print")
    def test_list_triggers_no_apps(self, mock_console_print):
        mock_response = Mock(spec=httpx.Response)
        mock_response.status_code = 200
        mock_response.ok = True
        mock_response.json.return_value = {"apps": []}
        self.mock_client.get_triggers.return_value = mock_response

        self.triggers_command.list_triggers()

        mock_console_print.assert_any_call("[yellow]No triggers found.[/yellow]")

    @patch("crewai.cli.triggers.main.console.print")
    def test_list_triggers_api_error(self, mock_console_print):
        self.mock_client.get_triggers.side_effect = Exception("API Error")

        with self.assertRaises(SystemExit):
            self.triggers_command.list_triggers()

        mock_console_print.assert_any_call("[bold red]Error fetching triggers: API Error[/bold red]")

    @patch("crewai.cli.triggers.main.console.print")
    def test_execute_with_trigger_invalid_format(self, mock_console_print):
        with self.assertRaises(SystemExit):
            self.triggers_command.execute_with_trigger("invalid-format")

        mock_console_print.assert_called_with(
            "[bold red]Error: Trigger must be in format 'app_slug/trigger_slug'[/bold red]"
        )

    @patch("crewai.cli.triggers.main.console.print")
    @patch.object(TriggersCommand, "_run_crew_with_payload")
    def test_execute_with_trigger_success(self, mock_run_crew, mock_console_print):
        mock_response = Mock(spec=httpx.Response)
        mock_response.status_code = 200
        mock_response.ok = True
        mock_response.json.return_value = {
            "sample_payload": {"key": "value", "data": "test"}
        }
        self.mock_client.get_trigger_payload.return_value = mock_response

        self.triggers_command.execute_with_trigger("test-app/test-trigger")

        self.mock_client.get_trigger_payload.assert_called_once_with("test-app", "test-trigger")
        mock_run_crew.assert_called_once_with({"key": "value", "data": "test"})
        mock_console_print.assert_any_call(
            "[bold blue]Fetching trigger payload for test-app/test-trigger...[/bold blue]"
        )

    @patch("crewai.cli.triggers.main.console.print")
    def test_execute_with_trigger_not_found(self, mock_console_print):
        mock_response = Mock(spec=httpx.Response)
        mock_response.status_code = 404
        mock_response.json.return_value = {"error": "Trigger not found"}
        self.mock_client.get_trigger_payload.return_value = mock_response

        with self.assertRaises(SystemExit):
            self.triggers_command.execute_with_trigger("test-app/nonexistent-trigger")

        mock_console_print.assert_any_call("[bold red]Error: Trigger not found[/bold red]")

    @patch("crewai.cli.triggers.main.console.print")
    def test_execute_with_trigger_api_error(self, mock_console_print):
        self.mock_client.get_trigger_payload.side_effect = Exception("API Error")

        with self.assertRaises(SystemExit):
            self.triggers_command.execute_with_trigger("test-app/test-trigger")

        mock_console_print.assert_any_call(
            "[bold red]Error executing crew with trigger: API Error[/bold red]"
        )


    @patch("subprocess.run")
    def test_run_crew_with_payload_success(self, mock_subprocess):
        payload = {"key": "value", "data": "test"}
        mock_subprocess.return_value = None

        self.triggers_command._run_crew_with_payload(payload)

        mock_subprocess.assert_called_once_with(
            ["uv", "run", "run_with_trigger", json.dumps(payload)],
            capture_output=False,
            text=True,
            check=True
        )

    @patch("subprocess.run")
    def test_run_crew_with_payload_failure(self, mock_subprocess):
        payload = {"key": "value"}
        mock_subprocess.side_effect = subprocess.CalledProcessError(1, "uv")

        with self.assertRaises(SystemExit):
            self.triggers_command._run_crew_with_payload(payload)

    @patch("subprocess.run")
    def test_run_crew_with_payload_empty_payload(self, mock_subprocess):
        payload = {}
        mock_subprocess.return_value = None

        self.triggers_command._run_crew_with_payload(payload)

        mock_subprocess.assert_called_once_with(
            ["uv", "run", "run_with_trigger", "{}"],
            capture_output=False,
            text=True,
            check=True
        )

    @patch("crewai.cli.triggers.main.console.print")
    def test_execute_with_trigger_with_default_error_message(self, mock_console_print):
        mock_response = Mock(spec=httpx.Response)
        mock_response.status_code = 404
        mock_response.json.return_value = {}
        self.mock_client.get_trigger_payload.return_value = mock_response

        with self.assertRaises(SystemExit):
            self.triggers_command.execute_with_trigger("test-app/test-trigger")

        mock_console_print.assert_any_call("[bold red]Error: Trigger not found[/bold red]")
