from unittest.mock import MagicMock, Mock, patch

from networkx import DiGraph

from django.test import SimpleTestCase

from routing.management.commands.routing_precompute import Command


def _make_graph() -> DiGraph:
    graph = DiGraph()
    graph.add_node(1, security=0.9, type="solar_system")
    graph.add_node(2, security=0.5, type="solar_system")
    graph.add_edge(1, 2, p_shortest=1, p_safest=2, p_less_safe=1, type="Stargate")
    return graph


class RoutingPrecomputeCommandTest(SimpleTestCase):
    @patch("routing.management.commands.routing_precompute.process_from_sde")
    @patch("routing.management.commands.routing_precompute.regenerate_connections")
    @patch("routing.management.commands.routing_precompute.build")
    @patch("builtins.open", new_callable=MagicMock)
    def test_handle_calls_build(self, _mock_open: Mock, mock_build: Mock, mock_regen: Mock, _mock_sde: Mock) -> None:
        mock_build.return_value = _make_graph()
        mock_file = MagicMock()
        _mock_open.return_value.__enter__.return_value = mock_file

        Command().handle()

        mock_regen.assert_called_once_with()
        mock_build.assert_called_once_with()

    @patch("routing.management.commands.routing_precompute.process_from_sde")
    @patch("routing.management.commands.routing_precompute.regenerate_connections")
    @patch("routing.management.commands.routing_precompute.build")
    @patch("builtins.open", new_callable=MagicMock)
    def test_handle_writes_to_file(self, mock_open: Mock, mock_build: Mock, _mock_regen: Mock, _mock_sde: Mock) -> None:
        mock_build.return_value = _make_graph()
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file

        Command().handle()

        mock_open.assert_called_once_with("routing_output.txt", "w")
        mock_file.write.assert_called_once()

    @patch("routing.management.commands.routing_precompute.process_from_sde")
    @patch("routing.management.commands.routing_precompute.regenerate_connections")
    @patch("routing.management.commands.routing_precompute.build")
    @patch("builtins.open", new_callable=MagicMock)
    def test_handle_writes_string_content(self, mock_open: Mock, mock_build: Mock, _mock_regen: Mock, _mock_sde: Mock) -> None:
        mock_build.return_value = _make_graph()
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file

        Command().handle()

        written = mock_file.write.call_args[0][0]
        self.assertIsInstance(written, str)
        self.assertGreater(len(written), 0)

    @patch("routing.management.commands.routing_precompute.process_from_sde")
    @patch("routing.management.commands.routing_precompute.regenerate_connections")
    @patch("routing.management.commands.routing_precompute.build")
    @patch("builtins.open", new_callable=MagicMock)
    def test_handle_content_contains_edge_data(self, mock_open: Mock, mock_build: Mock, _mock_regen: Mock, _mock_sde: Mock) -> None:
        mock_build.return_value = _make_graph()
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file

        Command().handle()

        written = mock_file.write.call_args[0][0]
        # to_dict_of_dicts output contains node IDs as keys
        self.assertIn("1", written)
        self.assertIn("2", written)

    @patch("routing.management.commands.routing_precompute.process_from_sde")
    @patch("routing.management.commands.routing_precompute.regenerate_connections")
    @patch("routing.management.commands.routing_precompute.build")
    @patch("builtins.open", new_callable=MagicMock)
    def test_handle_empty_graph_still_writes(self, mock_open: Mock, mock_build: Mock, _mock_regen: Mock, _mock_sde: Mock) -> None:
        mock_build.return_value = DiGraph()
        mock_file = MagicMock()
        mock_open.return_value.__enter__.return_value = mock_file

        Command().handle()

        mock_file.write.assert_called_once()
        mock_file.write.assert_called_once()
