# Standard Library
from unittest.mock import Mock, patch

# Django
from django.test import SimpleTestCase, TestCase

# Alliance Auth (Core)
from allianceauth.eveonline.models import EveAllianceInfo, EveCharacter, EveCorporationInfo

# Alliance Auth (External Libs)
from eve_sde.models import ItemCategory, ItemGroup, ItemType

from incursions.models.waitlist import (
    ApprovedFitting, ApprovedImplantSet, Badge, Ban, CharacterBadges, Fleet, WaitlistCategory, WaitlistCategoryRule,
)


class TestBanEntityName(SimpleTestCase):
    def test_entity_name_returns_character_name(self) -> None:
        character = EveCharacter(character_id=1, character_name="Alice Character")
        ban = Ban(entity_type=Ban.entity_choices.CHARACTER, entity_character=character)

        self.assertEqual(ban.entity_name, "Alice Character")

    def test_entity_name_returns_corporation_name(self) -> None:
        corporation = EveCorporationInfo(
            corporation_id=2, corporation_name="Test Corp", corporation_ticker="TEST",
        )
        ban = Ban(entity_type=Ban.entity_choices.CORPORATION, entity_corporation=corporation)

        self.assertEqual(ban.entity_name, "Test Corp")

    def test_entity_name_returns_alliance_name(self) -> None:
        alliance = EveAllianceInfo(alliance_id=3, alliance_name="Test Alliance", alliance_ticker="TSTA")
        ban = Ban(entity_type=Ban.entity_choices.ALLIANCE, entity_alliance=alliance)

        self.assertEqual(ban.entity_name, "Test Alliance")

    def test_entity_name_falls_back_to_unknown_when_entity_missing(self) -> None:
        ban = Ban(entity_type=Ban.entity_choices.CHARACTER, entity_character=None)

        self.assertEqual(ban.entity_name, "Unknown Name")

    def test_entity_name_falls_back_to_unknown_for_mismatched_type(self) -> None:
        # entity_type says Corporation but only a character is attached.
        character = EveCharacter(character_id=1, character_name="Alice Character")
        ban = Ban(entity_type=Ban.entity_choices.CORPORATION, entity_character=character, entity_corporation=None)

        self.assertEqual(ban.entity_name, "Unknown Name")


class TestBadgeMemberCount(TestCase):
    fixtures = ["waitlist_badges.json"]

    def setUp(self) -> None:
        self.character_one = EveCharacter.objects.create(character_id=101, character_name="Pilot One")
        self.character_two = EveCharacter.objects.create(character_id=102, character_name="Pilot Two")
        self.badge = Badge.objects.get(name="FC")

    def test_member_count_reflects_assigned_character_badges(self) -> None:
        self.assertEqual(self.badge.member_count, 0)

        CharacterBadges.objects.create(character=self.character_one, badge=self.badge)
        self.assertEqual(self.badge.member_count, 1)

        CharacterBadges.objects.create(character=self.character_two, badge=self.badge)
        self.assertEqual(self.badge.member_count, 2)


class TestApprovedFittingIsLogi(TestCase):
    def setUp(self) -> None:
        self.category = ItemCategory.objects.create(id=6, name="Ship", name_en="Ship", published=True)
        self.group = ItemGroup.objects.create(id=900, name="Logistics", name_en="Logistics", category=self.category, published=True)
        self.ship = ItemType.objects.create(id=1001, name="Basilisk", name_en="Basilisk", group=self.group, published=True)
        self.other_ship = ItemType.objects.create(id=1002, name="Vindicator", name_en="Vindicator", group=self.group, published=True)

        self.waitlist_category = WaitlistCategory.objects.create(name="LOGI")
        self.approved_fitting = ApprovedFitting.objects.create(ship=self.ship, name="Basilisk Fit", dna="1001::")
        self.other_fitting = ApprovedFitting.objects.create(ship=self.other_ship, name="Vindicator Fit", dna="1002::")

    def test_is_logi_true_when_rule_maps_to_logi_category(self) -> None:
        WaitlistCategoryRule.objects.create(waitlist_category=self.waitlist_category, ship=self.ship)

        self.assertTrue(self.approved_fitting.is_logi())

    def test_is_logi_false_when_rule_maps_to_other_category(self) -> None:
        other_category = WaitlistCategory.objects.create(name="DPS")
        WaitlistCategoryRule.objects.create(waitlist_category=other_category, ship=self.other_ship)

        self.assertFalse(self.other_fitting.is_logi())

    def test_is_logi_false_when_no_rule_exists(self) -> None:
        # DoesNotExist branch: no WaitlistCategoryRule created for this ship.
        self.assertFalse(self.approved_fitting.is_logi())

    @patch("incursions.models.waitlist.logger.debug")
    @patch("incursions.models.waitlist.WaitlistCategoryRule.objects.select_related")
    def test_is_logi_false_on_generic_exception(self, mock_select_related: Mock, mock_debug: Mock) -> None:
        mock_select_related.side_effect = RuntimeError("boom")

        self.assertFalse(self.approved_fitting.is_logi())
        mock_debug.assert_called_once()

    def test_implant_set_returns_none_string_without_implants(self) -> None:
        self.assertEqual(self.approved_fitting.implant_set(), "None")

    def test_implant_set_returns_set_name_when_present(self) -> None:
        implants = ApprovedImplantSet.objects.create(
            name="Amulet Set", set=ApprovedImplantSet.set_choices.AMULET, implants="[1,2,3]",
        )
        self.approved_fitting.implants = implants
        self.approved_fitting.save(update_fields=["implants"])

        self.assertEqual(self.approved_fitting.implant_set(), ApprovedImplantSet.set_choices.AMULET)


class TestFleetProviderWrappers(SimpleTestCase):
    def setUp(self) -> None:
        self.boss = EveCharacter(character_id=12345, character_name="Fleet Boss")
        self.fleet = Fleet(pk=99, boss=self.boss)

    @patch("incursions.models.waitlist.get_fleet_members")
    def test_get_fleet_members_delegates_to_provider(self, mock_get_fleet_members: Mock) -> None:
        mock_get_fleet_members.return_value = [Mock(character_id=1)]

        result = self.fleet.get_fleet_members()

        mock_get_fleet_members.assert_called_once_with(self.boss.character_id, self.fleet.pk)
        self.assertEqual(result, mock_get_fleet_members.return_value)

    @patch("incursions.models.waitlist.get_fleets_fleet_id_wings")
    def test_get_fleets_fleet_id_wings_delegates_to_provider(self, mock_get_wings: Mock) -> None:
        mock_get_wings.return_value = [Mock(id=1)]

        result = self.fleet.get_fleets_fleet_id_wings()

        mock_get_wings.assert_called_once_with(self.boss.character_id, self.fleet.pk)
        self.assertEqual(result, mock_get_wings.return_value)
