# Standard Library
from unittest.mock import Mock, patch

# Django
from django.test import RequestFactory

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

from incursions.api import notes as notes_api
from incursions.models.waitlist import CharacterNote


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 post(self, path: str, **kwargs: object):
        def decorator(func):
            self.routes[path] = func
            return func
        return decorator


def _routes() -> dict[str, object]:
    api = FakeAPI()
    notes_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 NotesAPITestBase(NoSocketsTestCase):
    def setUp(self) -> None:
        self.factory = RequestFactory()
        self.routes = _routes()
        self.character = EveCharacter.objects.create(character_id=1001, character_name="Pilot One")


class TestListNotes(NotesAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/notes"]

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

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

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

    def test_returns_empty_when_character_missing(self) -> None:
        request = self.factory.get("/notes")
        request.user = _user(**{"incursions.waitlist_notes_view": True})

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

        self.assertEqual(response.notes, [])

    def test_returns_notes_with_author(self) -> None:
        author = EveCharacter.objects.create(character_id=2002, character_name="Author")
        CharacterNote.objects.create(character=self.character, note="hi", author=author)
        request = self.factory.get("/notes")
        request.user = _user(**{"incursions.waitlist_notes_manage": True})

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

        self.assertEqual(len(response.notes), 1)
        self.assertEqual(response.notes[0].note, "hi")
        self.assertEqual(response.notes[0].author.character_id, author.character_id)

    def test_returns_notes_without_author(self) -> None:
        CharacterNote.objects.create(character=self.character, note="no author", author=None)
        request = self.factory.get("/notes")
        request.user = _user(**{"incursions.waitlist_notes_view": True})

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

        self.assertEqual(len(response.notes), 1)
        self.assertIsNone(response.notes[0].author)


class TestAddNote(NotesAPITestBase):
    def setUp(self) -> None:
        super().setUp()
        self.view = self.routes["/notes/add"]

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

        response = self.view(request, notes_api.AddNoteSchema(character_id=self.character.character_id, note="hi"))

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

    def test_returns_status_when_character_missing(self) -> None:
        request = self.factory.post("/notes/add")
        request.user = _user(**{"incursions.waitlist_notes_manage": True})

        response = self.view(request, notes_api.AddNoteSchema(character_id=999999, note="hi"))

        self.assertEqual(response, {"status": "Character not found"})

    @patch("incursions.api.notes.get_main_character_from_user")
    def test_creates_note(self, mock_main_char: Mock) -> None:
        author = EveCharacter.objects.create(character_id=2002, character_name="Author")
        mock_main_char.return_value = author
        request = self.factory.post("/notes/add")
        request.user = _user(**{"incursions.waitlist_notes_manage": True})

        response = self.view(request, notes_api.AddNoteSchema(character_id=self.character.character_id, note="hello"))

        self.assertEqual(response, {"status": "OK"})
        note = CharacterNote.objects.get()
        self.assertEqual(note.character, self.character)
        self.assertEqual(note.note, "hello")
        self.assertEqual(note.author, author)
