# Standard Library
from unittest.mock import Mock, patch

# Django
from django.test import SimpleTestCase

from incursions import providers


class TestProviders(SimpleTestCase):
    @patch("incursions.providers.Token.get_token", return_value="token")
    def test_get_token_delegates_to_token_model(self, mock_get_token: Mock) -> None:
        result = providers._get_token(123, [providers.READ_FLEET_SCOPE])

        self.assertEqual(result, "token")
        mock_get_token.assert_called_once_with(123, [providers.READ_FLEET_SCOPE])

    def test_get_incursions_incursions_returns_result_and_response(self) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = ("result", "response")
        mock_esi.client.Incursions.GetIncursions.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.get_incursions_incursions()

        self.assertEqual(result, ("result", "response"))
        mock_esi.client.Incursions.GetIncursions.assert_called_once_with()
        operation.result.assert_called_once_with(return_response=True)

    @patch("incursions.providers._get_token", return_value="token")
    def test_get_characters_character_id_fleet_uses_read_scope(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = {"fleet_id": 55}
        mock_esi.client.Fleets.GetCharactersCharacterIdFleet.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.get_characters_character_id_fleet(999)

        self.assertEqual(result, {"fleet_id": 55})
        mock_get_token.assert_called_once_with(999, [providers.READ_FLEET_SCOPE])
        mock_esi.client.Fleets.GetCharactersCharacterIdFleet.assert_called_once_with(
            character_id=999,
            token="token",
        )

    @patch("incursions.providers._get_token")
    def test_get_fleet_members_uses_valid_access_token(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.results.return_value = [{"character_id": 1}]
        operation.result.return_value = [{"character_id": 1}]
        mock_get_token.return_value = "token"
        mock_esi.client.Fleets.GetFleetsFleetIdMembers.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.get_fleet_members(5, 10)

        self.assertEqual(result, [{"character_id": 1}])
        mock_esi.client.Fleets.GetFleetsFleetIdMembers.assert_called_once_with(
            fleet_id=10,
            token="token",
        )
        operation.result.assert_called_once_with(use_etag=False)

    @patch("incursions.providers._get_token", return_value="token")
    def test_get_fleets_fleet_id_wings_uses_read_scope(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = {"wings": []}
        mock_esi.client.Fleets.GetFleetsFleetIdWings.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.get_fleets_fleet_id_wings(12, 34)

        self.assertEqual(result, {"wings": []})
        mock_get_token.assert_called_once_with(12, [providers.READ_FLEET_SCOPE])
        mock_esi.client.Fleets.GetFleetsFleetIdWings.assert_called_once_with(
            fleet_id=34,
            token="token",
        )

    @patch("incursions.providers._get_token", return_value="write-token")
    def test_kick_fleet_member_uses_write_scope(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = "ok"
        mock_esi.client.Fleets.DeleteFleetsFleetIdMembersMemberId.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.kick_fleet_member(8, 99, 77)

        self.assertEqual(result, "ok")
        mock_get_token.assert_called_once_with(8, [providers.WRITE_FLEET_SCOPE])
        mock_esi.client.Fleets.DeleteFleetsFleetIdMembersMemberId.assert_called_once_with(
            fleet_id=99,
            member_id=77,
            token="write-token",
        )
        operation.result.assert_called_once_with()

    @patch("incursions.providers.kick_fleet_member")
    @patch("incursions.providers.get_fleet_members")
    def test_kick_all_fleet_members_returns_count_even_on_errors(
        self,
        mock_get_fleet_members: Mock,
        mock_kick_fleet_member: Mock,
    ) -> None:
        mock_get_fleet_members.return_value = [
            Mock(character_id=11),
            Mock(character_id=22),
        ]
        mock_kick_fleet_member.side_effect = [None, RuntimeError("boom")]

        result = providers.kick_all_fleet_members(7, 99)

        self.assertEqual(result, 2)
        mock_get_fleet_members.assert_called_once_with(7, 99)
        self.assertEqual(mock_kick_fleet_member.call_count, 2)

    @patch("incursions.providers._get_token", return_value="search-token")
    def test_search_esi_joins_category_list_and_returns_response(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = ({"solar_system": [30000142]}, "response")
        mock_esi.client.Search.GetCharactersCharacterIdSearch.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.search_esi(44, "Jita", ["solar_system", "station"], strict=True)

        self.assertEqual(result, ({"solar_system": [30000142]}, "response"))
        mock_get_token.assert_called_once_with(44, [providers.SEARCH_SCOPE])
        mock_esi.client.Search.GetCharactersCharacterIdSearch.assert_called_once_with(
            character_id=44,
            categories="solar_system,station",
            search="Jita",
            strict=True,
            token="search-token",
        )
        operation.result.assert_called_once_with(return_response=True)

    @patch("incursions.providers._get_token", return_value="window-token")
    def test_open_window_information_posts_target_id(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = ("ok", "response")
        mock_esi.client.User_Interface.PostUiOpenwindowInformation.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.open_window_information(123, 456)

        self.assertEqual(result, ("ok", "response"))
        mock_get_token.assert_called_once_with(123, [providers.OPEN_WINDOW_SCOPE])
        mock_esi.client.User_Interface.PostUiOpenwindowInformation.assert_called_once_with(
            target_id=456,
            token="window-token",
        )
        operation.result.assert_called_once_with(return_response=True)

    @patch("incursions.providers._get_token", return_value="implants-token")
    def test_get_character_implants_uses_implants_scope(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = ([1, 2], "response")
        mock_esi.client.Clones.GetCharactersCharacterIdImplants.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.get_character_implants(321)

        self.assertEqual(result, ([1, 2], "response"))
        mock_get_token.assert_called_once_with(321, [providers.IMPLANTS_SCOPE])
        mock_esi.client.Clones.GetCharactersCharacterIdImplants.assert_called_once_with(
            character_id=321,
            token="implants-token",
        )
        operation.result.assert_called_once_with(return_response=True, use_etag=False)

    @patch("incursions.providers._get_token", return_value="invite-token")
    def test_invite_to_fleet_posts_invitation_body(self, mock_get_token: Mock) -> None:
        mock_esi = Mock()
        operation = Mock()
        operation.result.return_value = ("ok", "response")
        mock_esi.client.Fleets.PostFleetsFleetIdMembers.return_value = operation

        with patch("incursions.providers.esi", mock_esi):
            result = providers.invite_to_fleet(1, 2, 3, squad_id=None, wing_id=4, role="squad_member")

        self.assertEqual(result, ("ok", "response"))
        mock_get_token.assert_called_once_with(1, [providers.WRITE_FLEET_SCOPE])
        mock_esi.client.Fleets.PostFleetsFleetIdMembers.assert_called_once_with(
            body={
                "character_id": 3,
                "role": "squad_member",
                "squad_id": None,
                "wing_id": 4,
            },
            fleet_id=2,
            token="invite-token",
        )
        operation.result.assert_called_once_with(return_response=True)
