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

# Django
from django.http import Http404
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 waitlist as waitlist_api
from incursions.models.waitlist import (
    ActiveFleet, ApprovedFitting, Fitting, Fleet, FleetActivity, FleetSquad, ImplantSet, Waitlist, WaitlistCategory,
    WaitlistCategoryRule, WaitlistEntry, WaitlistEntryFit,
)


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

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

    def get(self, path: str, **kwargs: object):
        return self._register(path, **kwargs)

    def post(self, path: str, **kwargs: object):
        return self._register(path, **kwargs)

    def patch(self, path: str, **kwargs: object):
        return self._register(path, **kwargs)

    def delete(self, path: str, **kwargs: object):
        return self._register(path, **kwargs)


def _routes() -> dict[str, object]:
    api = FakeAPI()
    waitlist_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 WaitlistAPITestBase(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.boss = EveCharacter.objects.create(character_id=9000, character_name="Fleet Boss")
        self.fleet = Fleet.objects.create(boss=self.boss, is_updating=True, open=True, last_updated=now())
        active_fleet = ActiveFleet.get_solo()
        active_fleet.fleet = self.fleet
        active_fleet.save(update_fields=["fleet"])

        self.waitlist = Waitlist.get_solo()


class TestGetWaitlist(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist"]
        self.viewer = EveCharacter.objects.create(character_id=1001, character_name="Viewer")
        self.main_character = EveCharacter.objects.create(character_id=1002, character_name="Main Char")
        WaitlistCategory.objects.create(name="SPONGE")

    def _build_entry_fit(self) -> WaitlistEntryFit:
        entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.main_character)
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="[1,2]")
        category = WaitlistCategory.objects.get(name="SPONGE")
        return WaitlistEntryFit.objects.create(
            character=self.main_character, waitlist_entry=entry, fit=fitting, implant_set=implants,
            category=category, tags="", review_comment="looks fine", is_alt=False,
            messagexup="hello", cached_time_in_fleet=7200,
        )

    def test_denies_without_basic_waitlist_permission(self) -> None:
        request = self.factory.get("/waitlist")
        request.user = _user()

        response = self.view(request)

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

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_returns_closed_waitlist_with_categories(self, mock_main: Mock) -> None:
        mock_main.return_value = self.viewer
        request = self.factory.get("/waitlist")
        request.user = _user(**{"incursions.basic_waitlist": True})

        response = self.view(request)

        self.assertFalse(response.open)
        self.assertIsNone(response.waitlist)
        self.assertIn("SPONGE", response.categories)

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_is_self_reveals_full_fit_details(self, mock_main: Mock) -> None:
        self.waitlist.is_open = True
        self.waitlist.save(update_fields=["is_open"])
        self._build_entry_fit()
        mock_main.return_value = self.main_character  # Viewer IS the fit owner.
        request = self.factory.get("/waitlist")
        request.user = _user(**{"incursions.basic_waitlist": True})

        response = self.view(request)

        fit = response.waitlist[0].fits[0]
        self.assertEqual(fit.dna, "601::")
        self.assertEqual(fit.ship.name, "Sample Cruiser")
        self.assertEqual(fit.character.character_name, "Main Char")
        self.assertEqual(fit.review_comment, "looks fine")

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_non_self_viewer_without_context_flags_sees_hidden_fields(self, mock_main: Mock) -> None:
        self.waitlist.is_open = True
        self.waitlist.save(update_fields=["is_open"])
        self._build_entry_fit()
        mock_main.return_value = self.viewer  # Someone else entirely.
        request = self.factory.get("/waitlist")
        request.user = _user(**{"incursions.basic_waitlist": True})

        response = self.view(request)

        fit = response.waitlist[0].fits[0]
        self.assertEqual(fit.dna, "")
        self.assertEqual(fit.ship.name, "Hidden")
        self.assertEqual(fit.ship.id, 670)
        self.assertEqual(fit.character.character_name, "Hidden")
        self.assertIsNone(fit.review_comment)

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_context_ship_flag_reveals_ship_only(self, mock_main: Mock) -> None:
        self.waitlist.is_open = True
        self.waitlist.save(update_fields=["is_open"])
        self._build_entry_fit()
        mock_main.return_value = self.viewer
        request = self.factory.get("/waitlist")
        request.user = _user(**{"incursions.basic_waitlist": True, "incursions.waitlist_context_b": True})

        response = self.view(request)

        fit = response.waitlist[0].fits[0]
        self.assertEqual(fit.ship.name, "Sample Cruiser")
        self.assertEqual(fit.character.character_name, "Hidden")  # context_name not granted

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_context_wl_approver_reveals_everything(self, mock_main: Mock) -> None:
        self.waitlist.is_open = True
        self.waitlist.save(update_fields=["is_open"])
        self._build_entry_fit()
        mock_main.return_value = self.viewer
        request = self.factory.get("/waitlist")
        request.user = _user(**{"incursions.basic_waitlist": True, "incursions.waitlist_manage_waitlist": True})

        response = self.view(request)

        fit = response.waitlist[0].fits[0]
        self.assertEqual(fit.dna, "601::")
        self.assertEqual(fit.ship.name, "Sample Cruiser")
        self.assertEqual(fit.character.character_name, "Main Char")
        self.assertEqual(fit.review_comment, "looks fine")
        self.assertTrue(response.waitlist[0].can_remove)


class TestXup(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/xup"]
        self.character = EveCharacter.objects.create(character_id=2000, character_name="Xup Pilot")
        self.parser = Mock(ship=self.ship.id, to_dna=Mock(return_value="601::"))
        self.parser.to_analysis = Mock(return_value={"missing": {}, "extra": {}})

    def _request(self, **perms: bool):
        request = self.factory.post("/waitlist/xup")
        request.user = _user(**perms)
        return request

    def _payload(self) -> waitlist_api.XupRequest:
        return waitlist_api.XupRequest(
            waitlist_id=self.waitlist.pk, character_id=self.character.character_id,
            eft="[Sample Cruiser, Fit]\n", is_alt=False, messagexup="o7",
        )

    def test_denies_without_basic_waitlist_permission(self) -> None:
        response = self.view(self._request(), self._payload())

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

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_character_implants")
    @patch("incursions.api.waitlist.FittingParser.from_eft")
    def test_creates_new_xup_and_falls_back_to_sponge_category(
        self, mock_from_eft: Mock, mock_implants: Mock, mock_main_char: Mock, mock_submit: Mock,
    ) -> None:
        mock_from_eft.return_value = self.parser
        mock_implants.return_value = ([1, 2, 3], Mock())
        mock_main_char.side_effect = lambda character: character

        response = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())

        self.assertEqual(response[0], 200)
        self.assertEqual(response[1]["status"], "X-up recorded")
        fit = WaitlistEntryFit.objects.get(pk=response[1]["fit_id"])
        self.assertEqual(fit.category.name, "Sponge")
        self.assertEqual(fit.fit_analysis, {})  # No ApprovedFitting -> DoesNotExist branch.
        self.assertTrue(mock_submit.called)

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_character_implants")
    @patch("incursions.api.waitlist.FittingParser.from_eft")
    def test_uses_category_rule_when_present(
        self, mock_from_eft: Mock, mock_implants: Mock, mock_main_char: Mock, mock_submit: Mock,
    ) -> None:
        mock_from_eft.return_value = self.parser
        mock_implants.return_value = ([], Mock())
        mock_main_char.side_effect = lambda character: character
        bastion = WaitlistCategory.objects.create(name="BASTION")
        WaitlistCategoryRule.objects.create(waitlist_category=bastion, ship=self.ship)

        response = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())

        fit = WaitlistEntryFit.objects.get(pk=response[1]["fit_id"])
        self.assertEqual(fit.category.name, "BASTION")

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_character_implants")
    @patch("incursions.api.waitlist.FittingParser.from_eft")
    def test_fit_analysis_uses_approved_fitting_doctrine(
        self, mock_from_eft: Mock, mock_implants: Mock, mock_main_char: Mock, mock_submit: Mock,
    ) -> None:
        mock_from_eft.return_value = self.parser
        mock_implants.return_value = ([], Mock())
        mock_main_char.side_effect = lambda character: character
        ApprovedFitting.objects.create(ship=self.ship, name="Doctrine Fit", dna="601::")
        self.parser.to_analysis.return_value = {"missing": {1: 2}, "extra": {}}

        response = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())

        fit = WaitlistEntryFit.objects.get(pk=response[1]["fit_id"])
        self.assertEqual(fit.fit_analysis, {"missing": {"1": 2}, "extra": {}})

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_character_implants")
    @patch("incursions.api.waitlist.FittingParser.from_eft")
    def test_time_in_fleet_summed_from_fleet_activity(
        self, mock_from_eft: Mock, mock_implants: Mock, mock_main_char: Mock, mock_submit: Mock,
    ) -> None:
        mock_from_eft.return_value = self.parser
        mock_implants.return_value = ([], Mock())
        mock_main_char.side_effect = lambda character: character
        activity = FleetActivity.objects.create(
            fleet=self.fleet, character=self.character, ship=self.ship, last_seen=now(),
        )
        FleetActivity.objects.filter(pk=activity.pk).update(
            first_seen=now() - timedelta(hours=2), last_seen=now(),
        )

        response = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())

        fit = WaitlistEntryFit.objects.get(pk=response[1]["fit_id"])
        self.assertGreaterEqual(fit.cached_time_in_fleet, 7100)  # ~2 hours, allowing test runtime slack.

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_character_implants")
    @patch("incursions.api.waitlist.FittingParser.from_eft")
    def test_updates_existing_xup(
        self, mock_from_eft: Mock, mock_implants: Mock, mock_main_char: Mock, mock_submit: Mock,
    ) -> None:
        mock_from_eft.return_value = self.parser
        mock_implants.return_value = ([], Mock())
        mock_main_char.side_effect = lambda character: character

        first = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())
        second = self.view(self._request(**{"incursions.basic_waitlist": True}), self._payload())

        self.assertEqual(first[1]["status"], "X-up recorded")
        self.assertEqual(second[1]["status"], "X-up updated")
        self.assertEqual(first[1]["fit_id"], second[1]["fit_id"])


class TestRemoveWaitlistEntry(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/remove_waitlistentry"]
        self.main_character = EveCharacter.objects.create(character_id=3000, character_name="Entry Owner")
        self.entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.main_character)

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_returns_404_when_entry_missing(self, mock_submit: Mock) -> None:
        request = self.factory.post("/waitlist/remove_waitlistentry")
        request.user = _user()

        response = self.view(request, waitlist_api.RemoveWaitlistEntryRequest(id=999999))

        self.assertEqual(response, (404, {"error": "Waitlist entry not found"}))

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_self_can_remove_own_entry(self, mock_main: Mock, mock_submit: Mock) -> None:
        mock_main.return_value = self.main_character
        request = self.factory.post("/waitlist/remove_waitlistentry")
        request.user = _user()

        response = self.view(request, waitlist_api.RemoveWaitlistEntryRequest(id=self.entry.pk))

        self.assertEqual(response[0], 200)
        self.assertFalse(WaitlistEntry.objects.filter(pk=self.entry.pk).exists())

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_manager_can_remove_others_entry(self, mock_main: Mock, mock_submit: Mock) -> None:
        other_viewer = EveCharacter.objects.create(character_id=3001, character_name="Someone Else")
        mock_main.return_value = other_viewer
        request = self.factory.post("/waitlist/remove_waitlistentry")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.RemoveWaitlistEntryRequest(id=self.entry.pk))

        self.assertEqual(response[0], 200)
        self.assertFalse(WaitlistEntry.objects.filter(pk=self.entry.pk).exists())

    @patch("incursions.api.waitlist.get_main_character_from_user")
    def test_denies_non_self_non_manager(self, mock_main: Mock) -> None:
        other_viewer = EveCharacter.objects.create(character_id=3002, character_name="Nobody")
        mock_main.return_value = other_viewer
        request = self.factory.post("/waitlist/remove_waitlistentry")
        request.user = _user()

        response = self.view(request, waitlist_api.RemoveWaitlistEntryRequest(id=self.entry.pk))

        self.assertEqual(response, (403, {"error": "Permission denied"}))
        self.assertTrue(WaitlistEntry.objects.filter(pk=self.entry.pk).exists())


class TestRemoveWaitlistEntryFit(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/remove_waitlistentryfit"]
        self.character = EveCharacter.objects.create(character_id=4000, character_name="Fit Owner")
        entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.character)
        category = WaitlistCategory.objects.create(name="SPONGE")
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="[]")
        self.fit = WaitlistEntryFit.objects.create(
            character=self.character, waitlist_entry=entry, fit=fitting, implant_set=implants,
            category=category, tags="",
        )

    def test_raises_404_when_fit_missing(self) -> None:
        request = self.factory.post("/waitlist/remove_waitlistentryfit")
        request.user = _user()

        with self.assertRaises(Http404):
            self.view(request, waitlist_api.RemoveWaitlistEntryFitRequest(id=999999))

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.get_all_characters_from_user")
    def test_self_can_remove_own_fit(self, mock_all_chars: Mock, mock_submit: Mock) -> None:
        mock_all_chars.return_value = [self.character]
        request = self.factory.post("/waitlist/remove_waitlistentryfit")
        request.user = _user()

        response = self.view(request, waitlist_api.RemoveWaitlistEntryFitRequest(id=self.fit.pk))

        self.assertEqual(response[0], 200)
        self.assertFalse(WaitlistEntryFit.objects.filter(pk=self.fit.pk).exists())

    @patch("incursions.api.waitlist.get_all_characters_from_user")
    def test_denies_non_self_non_manager(self, mock_all_chars: Mock) -> None:
        mock_all_chars.return_value = []
        request = self.factory.post("/waitlist/remove_waitlistentryfit")
        request.user = _user()

        response = self.view(request, waitlist_api.RemoveWaitlistEntryFitRequest(id=self.fit.pk))

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


class TestInvite(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/invite"]
        self.character = EveCharacter.objects.create(character_id=5000, character_name="Invitee")
        entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.character)
        self.category = WaitlistCategory.objects.create(name="SPONGE")
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="[]")
        self.fit = WaitlistEntryFit.objects.create(
            character=self.character, waitlist_entry=entry, fit=fitting, implant_set=implants,
            category=self.category, tags="",
        )
        self.squad = FleetSquad.objects.create(fleet=self.fleet, category=self.category, wing_id=1, squad_id=2)

    def test_denies_without_manage_waitlist_permission(self) -> None:
        request = self.factory.post("/waitlist/invite")
        request.user = _user()

        response = self.view(request, waitlist_api.InviteRequest(id=self.fit.pk))

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

    @patch("incursions.api.waitlist.get_all_characters_from_user")
    def test_denies_when_requester_is_not_fleet_boss(self, mock_all_chars: Mock) -> None:
        mock_all_chars.return_value = []  # Boss not among requester's characters.
        request = self.factory.post("/waitlist/invite")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.InviteRequest(id=self.fit.pk))

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

    @patch("incursions.api.waitlist.sse_client.submit")
    @patch("incursions.api.waitlist.invite_to_fleet")
    @patch("incursions.api.waitlist.get_main_character_from_evecharacter")
    @patch("incursions.api.waitlist.get_all_characters_from_user")
    def test_invites_with_correct_squad_and_wing(
        self, mock_all_chars: Mock, mock_main_char: Mock, mock_invite: Mock, mock_submit: Mock,
    ) -> None:
        mock_all_chars.return_value = [self.boss]
        mock_main_char.return_value = self.character
        request = self.factory.post("/waitlist/invite")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.InviteRequest(id=self.fit.pk))

        self.assertEqual(response[0], 200)
        mock_invite.assert_called_once_with(
            boss_character_id=self.boss.character_id,
            fleet_id=self.fleet.pk,
            character_id=self.character.character_id,
            squad_id=self.squad.squad_id,
            wing_id=self.squad.wing_id,
            role="squad_member",
        )
        self.assertTrue(mock_submit.called)


class TestApproveRejectFit(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.approve_view = self.routes["/waitlist/approve"]
        self.reject_view = self.routes["/waitlist/reject"]
        self.character = EveCharacter.objects.create(character_id=6000, character_name="Fit Owner")
        entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.character)
        category = WaitlistCategory.objects.create(name="SPONGE")
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="[]")
        self.fit = WaitlistEntryFit.objects.create(
            character=self.character, waitlist_entry=entry, fit=fitting, implant_set=implants,
            category=category, tags="", approved=False,
        )

    def test_approve_denies_without_permission(self) -> None:
        request = self.factory.post("/waitlist/approve")
        request.user = _user()

        response = self.approve_view(request, waitlist_api.ApproveRequest(id=self.fit.pk))

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

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_approve_sets_approved_true(self, mock_submit: Mock) -> None:
        request = self.factory.post("/waitlist/approve")
        request.user = _user(**{"incursions.waitlist_manage_waitlist_approve_fits": True})

        response = self.approve_view(request, waitlist_api.ApproveRequest(id=self.fit.pk))

        self.fit.refresh_from_db()
        self.assertTrue(self.fit.approved)
        self.assertEqual(response["status"], f"Fit {self.fit.pk} approved")

    def test_reject_denies_without_permission(self) -> None:
        request = self.factory.post("/waitlist/reject")
        request.user = _user()

        response = self.reject_view(request, waitlist_api.RejectRequest(id=self.fit.pk, review_comment="no"))

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

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_reject_sets_approved_false_and_comment(self, mock_submit: Mock) -> None:
        self.fit.approved = True
        self.fit.save(update_fields=["approved"])
        request = self.factory.post("/waitlist/reject")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.reject_view(request, waitlist_api.RejectRequest(id=self.fit.pk, review_comment="bad fit"))

        self.fit.refresh_from_db()
        self.assertFalse(self.fit.approved)
        self.assertEqual(self.fit.review_comment, "bad fit")
        self.assertEqual(response, (200, {"status": f"Fit {self.fit.pk} rejected"}))


class TestSetOpen(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/set_open"]

    def test_denies_without_permission(self) -> None:
        request = self.factory.post("/waitlist/set_open")
        request.user = _user()

        response = self.view(request, waitlist_api.SetOpenRequest(open=True, waitlist_id=self.waitlist.pk))

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

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_opens_waitlist(self, mock_submit: Mock) -> None:
        request = self.factory.post("/waitlist/set_open")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.SetOpenRequest(open=True, waitlist_id=self.waitlist.pk))

        self.waitlist.refresh_from_db()
        self.assertTrue(self.waitlist.is_open)
        self.assertEqual(response, (200, "Waitlist is now Open."))
        self.assertEqual(mock_submit.call_count, 2)

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_closes_waitlist(self, mock_submit: Mock) -> None:
        request = self.factory.post("/waitlist/set_open")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.SetOpenRequest(open=False, waitlist_id=self.waitlist.pk))

        self.waitlist.refresh_from_db()
        self.assertFalse(self.waitlist.is_open)
        self.assertEqual(response, (200, "Waitlist is now Closed."))


class TestEmptyWaitlist(WaitlistAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/waitlist/empty"]
        self.character = EveCharacter.objects.create(character_id=7000, character_name="Some Pilot")
        self.entry = WaitlistEntry.objects.create(waitlist=self.waitlist, main_character=self.character)
        category = WaitlistCategory.objects.create(name="SPONGE")
        fitting = Fitting.objects.create(ship=self.ship, dna="601::")
        implants = ImplantSet.objects.create(implants="[]")
        WaitlistEntryFit.objects.create(
            character=self.character, waitlist_entry=self.entry, fit=fitting, implant_set=implants,
            category=category, tags="",
        )
        self.waitlist.is_open = True
        self.waitlist.save(update_fields=["is_open"])

    def test_denies_without_permission(self) -> None:
        request = self.factory.post("/waitlist/empty")
        request.user = _user()

        response = self.view(request, waitlist_api.EmptyWaitlistRequest(waitlist_id=self.waitlist.pk))

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

    @patch("incursions.api.waitlist.sse_client.submit")
    def test_empties_waitlist_and_closes_it(self, mock_submit: Mock) -> None:
        request = self.factory.post("/waitlist/empty")
        request.user = _user(**{"incursions.waitlist_manage_waitlist": True})

        response = self.view(request, waitlist_api.EmptyWaitlistRequest(waitlist_id=self.waitlist.pk))

        self.assertEqual(response, (200, {"status": "OK"}))
        self.assertFalse(WaitlistEntry.objects.filter(waitlist=self.waitlist).exists())
        self.waitlist.refresh_from_db()
        self.assertFalse(self.waitlist.is_open)
