import argparse
import unittest

from unittest.mock import Mock, patch

from data_exchange_agent.__main__ import main


class TestMain(unittest.TestCase):
    """
    Test suite for the main application entry point.

    This test class validates the main() function behavior, including:
    - Command-line argument parsing and validation
    - Flask application initialization with correct configuration
    - Server startup with proper host, port, and worker settings
    - Debug mode handling and development server configuration
    - Integration between CLI arguments and application config
    - Proper error handling for invalid arguments

    Tests use extensive mocking to avoid actually starting servers during
    test execution, ensuring fast and reliable test runs.
    """

    @patch("data_exchange_agent.__main__.create_container")
    @patch("data_exchange_agent.__main__.FlaskApp")
    @patch("data_exchange_agent.__main__.create_argument_parser")
    def test_main_creates_flask_app_and_starts_server(self, mock_parser_fn, mock_flask_app_class, mock_create_container):
        """
        Test that main() creates FlaskApp and starts server with correct configuration.

        Validates the complete application startup process:
        - Argument parser creation and configuration
        - Command-line argument parsing with expected defaults
        - FlaskApp instantiation with parsed configuration
        - Server startup with proper parameters

        Args:
            mock_parser_fn: Mock for create_argument_parser function
            mock_flask_app_class: Mock for FlaskApp class
            mock_create_container: Mock for create_container function

        """
        mock_parser = Mock()
        mock_parser_fn.return_value = mock_parser

        mock_args = argparse.Namespace(
            command=None,
            config=None,
            max_parallel_tasks=4,
            interval=120,
            host="0.0.0.0",
            port=5001,
            debug=False,
        )
        mock_parser.parse_args.return_value = mock_args

        mock_flask_app = Mock()
        mock_flask_app_class.return_value = mock_flask_app

        main()

        mock_create_container.assert_called_once_with(mock_args, config_path=None)

        mock_flask_app_class.assert_called_once()

        mock_flask_app.create_app.assert_called_once_with()
        mock_flask_app.start_server.assert_called_once()

    @patch("data_exchange_agent.__main__.create_container")
    @patch("data_exchange_agent.__main__.FlaskApp")
    @patch("data_exchange_agent.__main__.create_argument_parser")
    def test_main_with_debug_mode(self, mock_parser_fn, mock_flask_app_class, mock_create_container):
        """Test main with debug mode enabled."""
        mock_parser = Mock()
        mock_parser_fn.return_value = mock_parser

        mock_args = argparse.Namespace(
            command=None,
            config=None,
            max_parallel_tasks=2,
            interval=60,
            host="127.0.0.1",
            port=8000,
            debug=True,
        )
        mock_parser.parse_args.return_value = mock_args

        mock_flask_app = Mock()
        mock_flask_app_class.return_value = mock_flask_app

        main()

        mock_create_container.assert_called_once_with(mock_args, config_path=None)
        mock_flask_app.create_app.assert_called_once_with()

    @patch("data_exchange_agent.__main__.create_container")
    @patch("data_exchange_agent.__main__.FlaskApp")
    @patch("data_exchange_agent.__main__.create_argument_parser")
    def test_main_argument_parser_setup(self, mock_parser_fn, mock_flask_app_class, mock_create_container):
        """Test that argument parser is set up correctly."""
        mock_parser = Mock()
        mock_parser_fn.return_value = mock_parser
        mock_parser.parse_args.return_value = argparse.Namespace(
            command=None,
            config=None,
            max_parallel_tasks=4,
            interval=120,
            host="0.0.0.0",
            port=5001,
            debug=False,
        )
        mock_flask_app_class.return_value = Mock()

        main()

        # Verify create_argument_parser was called
        mock_parser_fn.assert_called_once()

    @patch("data_exchange_agent.__main__.create_container")
    @patch("data_exchange_agent.__main__.FlaskApp")
    @patch("data_exchange_agent.__main__.create_argument_parser")
    def test_main_with_exception_handling(self, mock_parser_fn, mock_flask_app_class, mock_create_container):
        """Test that main handles exceptions properly with decorator in _run_server."""
        mock_parser = Mock()
        mock_parser_fn.return_value = mock_parser

        mock_args = argparse.Namespace(
            command=None,
            config=None,
            max_parallel_tasks=4,
            interval=120,
            host="0.0.0.0",
            port=5001,
            debug=False,
        )
        mock_parser.parse_args.return_value = mock_args

        # Make create_container raise an exception (this happens inside _run_server)
        mock_create_container.side_effect = Exception("Test exception")

        with patch("builtins.print") as mock_print:
            main()
            # The print_error_with_message decorator catches the exception and prints it
            mock_print.assert_any_call("Error starting the Data Exchange Agent application.")
            mock_print.assert_any_call("Unexpected error: Test exception")

    @patch("data_exchange_agent.__main__.create_container")
    @patch("data_exchange_agent.__main__.FlaskApp")
    @patch(
        "sys.argv",
        ["main.py", "-w", "8", "-i", "60", "--host", "192.168.1.1", "-p", "9000", "-d"],
    )
    def test_main_with_command_line_args(self, mock_flask_app_class, mock_create_container):
        """Test main with actual command line arguments."""
        mock_flask_app = Mock()
        mock_flask_app_class.return_value = mock_flask_app

        main()

        # The actual call will include command and config from the argument parser
        call_args = mock_create_container.call_args
        actual_args = call_args[0][0]

        # Verify the key arguments are correct
        self.assertEqual(actual_args.max_parallel_tasks, 8)
        self.assertEqual(actual_args.interval, 60)
        self.assertEqual(actual_args.host, "192.168.1.1")
        self.assertEqual(actual_args.port, 9000)
        self.assertEqual(actual_args.debug, True)

        mock_flask_app.create_app.assert_called_once_with()


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