# Standard Library
from unittest.mock import Mock, patch

# Third Party
from corptools.models import CharacterAudit, Skill

# Django
from django.http import Http404
from django.test import RequestFactory

# 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 skills as skills_api
from incursions.models.waitlist import ApprovedSkills, SkillCheck


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()
    skills_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 SkillsAPITestBase(NoSocketsTestCase):
    def setUp(self) -> None:
        self.factory = RequestFactory()
        self.routes = _routes()
        self.view = self.routes["/skills/{character_id}"]
        self.character = EveCharacter.objects.create(character_id=1001, character_name="Pilot One")

        skill_category = ItemCategory.objects.create(id=16, name="Skill", name_en="Skill", published=True)
        self.skill_group = ItemGroup.objects.create(id=255, name="Gunnery", name_en="Gunnery", category=skill_category, published=True)
        self.skill_type = ItemType.objects.create(id=3300, name="Small Hybrid Turret", name_en="Small Hybrid Turret", group=self.skill_group, published=True)

        ship_category = ItemCategory.objects.create(id=6, name="Ship", name_en="Ship", published=True)
        ship_group = ItemGroup.objects.create(id=600, name="Cruiser", name_en="Cruiser", category=ship_category, published=True)
        self.ship = ItemType.objects.create(id=601, name="Sample Cruiser", name_en="Sample Cruiser", group=ship_group, published=True)

    @patch("incursions.api.skills.get_all_characters_from_user", return_value=[])
    def test_denies_without_permission_or_ownership(self, mock_alts: Mock) -> None:
        request = self.factory.get(f"/skills/{self.character.character_id}")
        request.user = _user()

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

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

    @patch("incursions.api.skills.get_all_characters_from_user", return_value=[])
    def test_raises_404_when_character_missing(self, mock_alts: Mock) -> None:
        request = self.factory.get("/skills/999999")
        request.user = _user(**{"incursions.waitlist_skills_view": True})

        with self.assertRaises(Http404):
            self.view(request, character_id=999999)

    @patch("incursions.api.skills.get_all_characters_from_user", return_value=[])
    def test_raises_404_when_character_audit_missing(self, mock_alts: Mock) -> None:
        request = self.factory.get(f"/skills/{self.character.character_id}")
        request.user = _user(**{"incursions.waitlist_skills_view": True})

        with self.assertRaises(Http404):
            self.view(request, character_id=self.character.character_id)

    @patch("incursions.api.skills.get_all_characters_from_user", return_value=[])
    def test_returns_skills_response(self, mock_alts: Mock) -> None:
        audit = CharacterAudit.objects.create(character=self.character, active=True)
        Skill.objects.create(character=audit, skill_id=self.skill_type.id, skill_name=self.skill_type, active_skill_level=4, skillpoints_in_skill=1000, trained_skill_level=4)
        skill_check = SkillCheck.objects.create(skill=self.skill_type, min=3, elite=4, gold=5)
        approved = ApprovedSkills.objects.create(ship=self.ship)
        approved.skill_checks.add(skill_check)
        request = self.factory.get(f"/skills/{self.character.character_id}")
        request.user = _user(**{"incursions.waitlist_skills_view": True})

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

        self.assertEqual(status_code, 200)
        self.assertEqual(response.current[self.skill_type.id], 4)
        self.assertEqual(response.ids["Small Hybrid Turret"], self.skill_type.id)
        self.assertIn("Gunnery", response.categories)
        self.assertIn(self.skill_type.id, response.categories["Gunnery"])
        self.assertEqual(response.requirements["Sample Cruiser"][self.skill_type.id].min, 3)

    @patch("incursions.api.skills.get_all_characters_from_user")
    def test_allows_own_character_without_permission(self, mock_alts: Mock) -> None:
        mock_alts.return_value = [self.character]
        CharacterAudit.objects.create(character=self.character, active=True)
        request = self.factory.get(f"/skills/{self.character.character_id}")
        request.user = _user()

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

        self.assertEqual(status_code, 200)
