# Standard Library
from datetime import datetime, timedelta, timezone
from unittest.mock import Mock

# Django
from django.test import RequestFactory
from django.utils.timezone import now

# Alliance Auth (Core)
from allianceauth.eveonline.models import EveCharacter
from allianceauth.utils.testing import NoSocketsTestCase

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

from incursions.api import history as history_api
from incursions.models.waitlist import Fitting, FittingHistory, Fleet, FleetActivity, ImplantSet


class FakeAPI:
    def __init__(self) -> None:
        self.routes: dict[str, object] = {}

    def get(self, path: str, **kwargs: object):
        def decorator(func):
            self.routes[path] = func
            return func
        return decorator


def _routes() -> dict[str, object]:
    api = FakeAPI()
    history_api.setup(api)
    return api.routes


def _user(**perms: bool) -> Mock:
    return Mock(has_perm=Mock(side_effect=lambda perm: perms.get(perm, False)))


class HistoryAPITestBase(NoSocketsTestCase):
    def setUp(self) -> None:
        self.factory = RequestFactory()
        self.routes = _routes()
        category = ItemCategory.objects.create(id=6, name="Ship", name_en="Ship", published=True)
        group = ItemGroup.objects.create(id=600, name="Cruiser", name_en="Cruiser", category=category, published=True)
        self.ship = ItemType.objects.create(id=601, name="Sample Cruiser", name_en="Sample Cruiser", group=group, published=True)
        self.character = EveCharacter.objects.create(character_id=1001, character_name="Pilot One")


class TestFleetHistory(HistoryAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/history/fleet"]

    def test_denies_without_permission(self) -> None:
        request = self.factory.get("/history/fleet")
        request.user = _user()

        response = self.view(request, character_id=self.character.character_id)

        self.assertEqual(response, (403, {"error": "Permission denied"}))

    def test_returns_activity_and_summary(self) -> None:
        boss = EveCharacter.objects.create(character_id=9000, character_name="Boss")
        fleet = Fleet.objects.create(boss=boss, is_updating=True, open=True, last_updated=now())
        first_seen = now() - timedelta(hours=3)
        last_seen = now() - timedelta(hours=1)
        activity = FleetActivity.objects.create(fleet=fleet, character=self.character, ship=self.ship, last_seen=last_seen)
        FleetActivity.objects.filter(pk=activity.pk).update(first_seen=first_seen)
        request = self.factory.get("/history/fleet")
        request.user = _user(**{"incursions.waitlist_history_view": True})

        status_code, response = self.view(request, character_id=self.character.character_id)

        self.assertEqual(status_code, 200)
        self.assertEqual(len(response.activity), 1)
        self.assertEqual(response.activity[0].ship.name, "Sample Cruiser")
        self.assertEqual(response.activity[0].time_in_fleet, 7200)
        self.assertEqual(len(response.summary), 1)
        self.assertEqual(response.summary[0].time_in_fleet, 7200)


class TestFleetComp(HistoryAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/history/fleet-comp"]

    def test_denies_without_permission(self) -> None:
        request = self.factory.get("/history/fleet-comp")
        request.user = _user()

        response = self.view(request, time=0)

        self.assertEqual(response, (403, {"error": "Permission denied"}))

    def test_returns_fleet_composition_at_time(self) -> None:
        boss = EveCharacter.objects.create(character_id=9000, character_name="Boss")
        fleet = Fleet.objects.create(boss=boss, is_updating=True, open=True, last_updated=now())
        time_point = datetime(2026, 3, 1, 12, 0, tzinfo=timezone.utc)
        activity = FleetActivity.objects.create(
            fleet=fleet, character=self.character, ship=self.ship,
            last_seen=time_point + timedelta(hours=1),
        )
        FleetActivity.objects.filter(pk=activity.pk).update(first_seen=time_point - timedelta(hours=1))
        request = self.factory.get("/history/fleet-comp")
        request.user = _user(**{"incursions.waitlist_history_view": True})

        status_code, response = self.view(request, time=int(time_point.timestamp()))

        self.assertEqual(status_code, 200)
        entries = response.fleets[fleet.pk]
        self.assertEqual(len(entries), 1)
        self.assertEqual(entries[0].character.character_id, self.character.character_id)
        self.assertFalse(entries[0].is_boss)

    def test_flags_boss_entry(self) -> None:
        boss = EveCharacter.objects.create(character_id=9000, character_name="Boss")
        fleet = Fleet.objects.create(boss=boss, is_updating=True, open=True, last_updated=now())
        time_point = datetime(2026, 3, 1, 12, 0, tzinfo=timezone.utc)
        activity = FleetActivity.objects.create(
            fleet=fleet, character=boss, ship=self.ship,
            last_seen=time_point + timedelta(hours=1),
        )
        FleetActivity.objects.filter(pk=activity.pk).update(first_seen=time_point - timedelta(hours=1))
        request = self.factory.get("/history/fleet-comp")
        request.user = _user(**{"incursions.waitlist_history_view": True})

        status_code, response = self.view(request, time=int(time_point.timestamp()))

        self.assertEqual(status_code, 200)
        self.assertTrue(response.fleets[fleet.pk][0].is_boss)


class TestXupHistory(HistoryAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/history/xup"]

    def test_denies_without_permission(self) -> None:
        request = self.factory.get("/history/xup")
        request.user = _user()

        response = self.view(request, character_id=self.character.character_id)

        self.assertEqual(response, (403, {"error": "Permission denied"}))

    def test_returns_xup_history_with_parsed_implants(self) -> None:
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="1:2:3")
        FittingHistory.objects.create(character=self.character, fit=fitting, implant_set=implants)
        request = self.factory.get("/history/xup")
        request.user = _user(**{"incursions.waitlist_history_view": True})

        status_code, response = self.view(request, character_id=self.character.character_id)

        self.assertEqual(status_code, 200)
        self.assertEqual(len(response.xups), 1)
        self.assertEqual(response.xups[0].implants, [1, 2, 3])
        self.assertEqual(response.xups[0].dna, "601::")
        self.assertEqual(response.xups[0].ship.name, "Sample Cruiser")

    def test_handles_unparsable_implants_gracefully(self) -> None:
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="not-a-number")
        FittingHistory.objects.create(character=self.character, fit=fitting, implant_set=implants)
        request = self.factory.get("/history/xup")
        request.user = _user(**{"incursions.waitlist_history_view": True})

        status_code, response = self.view(request, character_id=self.character.character_id)

        self.assertEqual(status_code, 200)
        self.assertEqual(response.xups[0].implants, [])
