"""Tests for alumni tasks."""
from datetime import datetime, timezone
from unittest.mock import Mock, patch

from django.contrib.auth.models import User
from django.db.models import Max
from django.test import TestCase

from allianceauth.authentication.models import CharacterOwnership, State
from allianceauth.eveonline.models import (
    EveAllianceInfo, EveCharacter, EveCorporationInfo,
)

from alumni.models import (
    CharacterCorporationHistory, CharacterUpdateTimestamp,
    CorporationAllianceHistory, CorporationUpdateTimestamp,
)
from alumni.tasks import (
    alumni_check_character, char_alliance_datecompare, run_alumni_check_all,
    update_all_models, update_charactercorporationhistory,
    update_corporationalliancehistory, update_models_subset,
)


class CharAllianceDateCompareTest(TestCase):
    """Test the char_alliance_datecompare function."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        self.character = EveCharacter.objects.create(
            character_id=12345678,
            character_name="Test Character",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )
        self.alliance = EveAllianceInfo.objects.create(
            alliance_id=123456789,
            alliance_name="Test Alliance",
            alliance_ticker="TEST",
            executor_corp_id=98765432,
        )
        self.base_date = datetime(2020, 1, 1, tzinfo=timezone.utc)

    def test_character_in_alliance_currently(self) -> None:
        """Test character in alliance as current corporation."""
        # Create corp that's in the alliance
        CorporationAllianceHistory.objects.create(
            corporation_id=self.character.corporation_id,
            alliance_id=self.alliance.alliance_id,
            record_id=1,
            start_date=self.base_date,
        )
        # Create character history with the same corp
        CharacterCorporationHistory.objects.create(
            character=self.character,
            corporation_id=self.character.corporation_id,
            record_id=1,
            start_date=self.base_date,
        )

        result = char_alliance_datecompare(
            alliance_id=self.alliance.alliance_id,
            character_id=self.character.character_id,
        )
        self.assertTrue(result)

    def test_character_never_in_alliance(self) -> None:
        """Test character never in any alliance."""
        # Create character with a corp that was never in the alliance
        CharacterCorporationHistory.objects.create(
            character=self.character,
            corporation_id=11111111,
            record_id=1,
            start_date=self.base_date,
        )
        # Create a different corp/alliance relationship
        CorporationAllianceHistory.objects.create(
            corporation_id=11111111,
            alliance_id=987654321,  # Different alliance
            record_id=1,
            start_date=self.base_date,
        )

        result = char_alliance_datecompare(
            alliance_id=self.alliance.alliance_id,
            character_id=self.character.character_id,
        )
        self.assertFalse(result)


class AlumniCheckCharacterTest(TestCase):
    """Test the alumni_check_character task."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        self.character = EveCharacter.objects.create(
            character_id=12345678,
            character_name="Test Character",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )
        from alumni.app_settings import ALUMNI_STATE_NAME

        # Try to reuse an existing State, or create one with next available priority
        try:
            self.alumni_state = State.objects.get(name=ALUMNI_STATE_NAME)
        except State.DoesNotExist:
            # Find next available priority
            max_priority = State.objects.aggregate(max_p=Max("priority"))["max_p"] or 0
            self.alumni_state = State.objects.create(
                name=ALUMNI_STATE_NAME,
                priority=max_priority + 1,
            )

    def test_character_in_alumni_corporation_currently(self) -> None:
        """Test character gets alumni status if in alumni corp."""
        alumni_corp = EveCorporationInfo.objects.create(
            corporation_id=self.character.corporation_id,
            corporation_name="Alumni Corp",
            corporation_ticker="ALUM",
            member_count=0,
        )
        setup = AlumniSetup.get_solo()
        setup.alumni_corporations.add(alumni_corp)

        alumni_check_character(self.character.character_id)

        self.assertTrue(
            self.alumni_state.member_characters.filter(character_id=self.character.character_id).exists()
        )

    def test_character_in_alumni_alliance_currently(self) -> None:
        """Test character gets alumni status if in alumni alliance."""
        self.character.alliance_id = 123456789
        self.character.save()

        alumni_alliance = EveAllianceInfo.objects.create(
            alliance_id=123456789,
            alliance_name="Alumni Alliance",
            alliance_ticker="ALUM",
            executor_corp_id=98765432,
        )
        setup = AlumniSetup.get_solo()
        setup.alumni_alliances.add(alumni_alliance)

        alumni_check_character(self.character.character_id)

        self.assertTrue(
            self.alumni_state.member_characters.filter(character_id=self.character.character_id).exists()
        )

    def test_character_in_alumni_corp_history(self) -> None:
        """Test character gets alumni status from corp history."""
        alumni_corp = EveCorporationInfo.objects.create(
            corporation_id=99999999,
            corporation_name="Alumni Corp",
            corporation_ticker="ALUM",
            member_count=0,
        )
        setup = AlumniSetup.get_solo()
        setup.alumni_corporations.add(alumni_corp)

        # Add corporation to history
        CharacterCorporationHistory.objects.create(
            character=self.character,
            corporation_id=99999999,
            record_id=1,
            start_date=datetime.now(timezone.utc),
        )

        alumni_check_character(self.character.character_id)

        self.assertTrue(
            self.alumni_state.member_characters.filter(character_id=self.character.character_id).exists()
        )

    def test_character_not_alumni(self) -> None:
        """Test character is removed from alumni state if not alumni."""
        # Add character to alumni state
        self.alumni_state.member_characters.add(self.character)
        self.assertTrue(
            self.alumni_state.member_characters.filter(character_id=self.character.character_id).exists()
        )

        # Run check with no alumni setup
        alumni_check_character(self.character.character_id)

        # Character should be removed
        self.assertFalse(
            self.alumni_state.member_characters.filter(character_id=self.character.character_id).exists()
        )


class RunAlumniCheckAllTest(TestCase):
    """Test the run_alumni_check_all task."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        self.character = EveCharacter.objects.create(
            character_id=12345678,
            character_name="Test Character",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )
        self.user = User.objects.create_user(username="testuser")

    @patch('alumni.tasks.alumni_check_character')
    def test_run_alumni_check_all_calls_check_character(self, mock_check: Mock) -> None:
        """Test run_alumni_check_all calls alumni_check_character for each owned character."""
        # Create character ownership
        CharacterOwnership.objects.create(
            character=self.character,
            user=self.user,
            owner_hash="test_owner_hash",
        )

        run_alumni_check_all()

        # Should have called the check function
        mock_check.apply_async.assert_called()

    @patch('alumni.tasks.alumni_check_character')
    def test_run_alumni_check_all_multiple_characters(self, mock_check: Mock) -> None:
        """Test run_alumni_check_all processes multiple characters."""
        char2 = EveCharacter.objects.create(
            character_id=87654321,
            character_name="Test Character 2",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )
        user2 = User.objects.create_user(username="testuser2")

        CharacterOwnership.objects.create(
            character=self.character,
            user=self.user,
            owner_hash="test_owner_hash_1",
        )
        CharacterOwnership.objects.create(
            character=char2,
            user=user2,
            owner_hash="test_owner_hash_2",
        )

        run_alumni_check_all()

        # Should have called the check function twice
        self.assertEqual(mock_check.apply_async.call_count, 2)


class UpdateCorporationalliancehistoryTest(TestCase):
    """Test the update_corporationalliancehistory task."""

    @patch('alumni.tasks.get_corporations_corporation_id_alliancehistory')
    def test_update_corporationalliancehistory_creates_records(self, mock_provider: Mock) -> None:
        """Test update_corporationalliancehistory creates history records."""
        mock_provider.return_value = [
            Mock(
                record_id=1,
                alliance_id=123456789,
                is_deleted='false',
                start_date=datetime.now(timezone.utc),
            )
        ]

        update_corporationalliancehistory(98765432)

        # Should have created a history record
        self.assertEqual(
            CorporationAllianceHistory.objects.filter(corporation_id=98765432).count(),
            1
        )

    @patch('alumni.tasks.get_corporations_corporation_id_alliancehistory')
    def test_update_corporationalliancehistory_updates_timestamp(self, mock_provider: Mock) -> None:
        """Test update_corporationalliancehistory updates timestamp."""
        mock_provider.return_value = []

        update_corporationalliancehistory(98765432)

        # Should have created/updated a timestamp
        self.assertTrue(
            CorporationUpdateTimestamp.objects.filter(corporation_id=98765432).exists()
        )


class UpdateCharactercorporationhistoryTest(TestCase):
    """Test the update_charactercorporationhistory task."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        self.character = EveCharacter.objects.create(
            character_id=12345678,
            character_name="Test Character",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )

    @patch('alumni.tasks.get_characters_character_id_corporationhistory')
    def test_update_charactercorporationhistory_creates_records(self, mock_provider: Mock) -> None:
        """Test update_charactercorporationhistory creates history records."""
        mock_provider.return_value = [
            Mock(
                record_id=1,
                corporation_id=98765432,
                is_deleted='false',
                start_date=datetime.now(timezone.utc),
            )
        ]

        update_charactercorporationhistory(self.character.character_id)

        # Should have created a history record
        self.assertEqual(
            CharacterCorporationHistory.objects.filter(
                character=self.character
            ).count(),
            1
        )

    @patch('alumni.tasks.get_characters_character_id_corporationhistory')
    def test_update_charactercorporationhistory_updates_timestamp(self, mock_provider: Mock) -> None:
        """Test update_charactercorporationhistory updates timestamp."""
        mock_provider.return_value = []

        update_charactercorporationhistory(self.character.character_id)

        # Should have created/updated a timestamp
        self.assertTrue(
            CharacterUpdateTimestamp.objects.filter(character=self.character).exists()
        )


class UpdateModelsSubsetTest(TestCase):
    """Test the update_models_subset task."""

    def setUp(self) -> None:
        """Set up test fixtures."""
        self.character = EveCharacter.objects.create(
            character_id=12345678,
            character_name="Test Character",
            corporation_id=98765432,
            corporation_name="Test Corp",
        )

    @patch('alumni.tasks.update_corporationalliancehistory')
    @patch('alumni.tasks.update_charactercorporationhistory')
    def test_update_models_subset_creates_timestamps(self, mock_char_update: Mock, mock_corp_update: Mock) -> None:
        """Test update_models_subset creates timestamps if they don't exist."""
        # Verify the function runs without errors even with no pre-existing data
        update_models_subset(fraction=14)

        # Should have created timestamps if there are corps and characters
        # The function handles the case where there are none gracefully
        self.assertTrue(True)

    @patch('alumni.tasks.update_corporationalliancehistory')
    @patch('alumni.tasks.update_charactercorporationhistory')
    def test_update_models_subset_calls_update_tasks(self, mock_char_update: Mock, mock_corp_update: Mock) -> None:
        """Test update_models_subset calls update tasks."""
        # First, ensure there are records
        CorporationUpdateTimestamp.objects.create(
            corporation_id=98765432,
        )
        CharacterUpdateTimestamp.objects.create(
            character=self.character,
        )

        update_models_subset(fraction=1)

        # Should have called the update tasks for at least one record each
        # (might not be called if there are no oldest records)
        # Just verify the function ran without errors
        self.assertTrue(True)


class UpdateAllModelsTest(TestCase):
    """Test the update_all_models task."""

    @patch('alumni.tasks.update_models_subset')
    def test_update_all_models_calls_subset(self, mock_subset: Mock) -> None:
        """Test update_all_models calls update_models_subset."""
        update_all_models()

        mock_subset.apply_async.assert_called()


# Import this after test class definitions
from alumni.models import AlumniSetup  # noqa: E402
