"""
Tests unitaires pour le module School.
Vérifie que tous les endpoints (Speciality, Teacher, SchoolClass, Planning,
Fee, Discount, PaymentPlan, PaymentMode, Competency, EstablishmentCompetency)
requièrent une authentification JWT.
"""

from django.test import TestCase, override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken

from Auth.models import Profile


def create_user(email="school_test@example.com", password="testpassword123"):
    return Profile.objects.create_user(username=email, email=email, password=password)


def get_jwt_token(user):
    refresh = RefreshToken.for_user(user)
    return str(refresh.access_token)


TEST_REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
}

TEST_CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}

OVERRIDE_SETTINGS = dict(
    CACHES=TEST_CACHES,
    SESSION_ENGINE='django.contrib.sessions.backends.db',
    REST_FRAMEWORK=TEST_REST_FRAMEWORK,
)


def _assert_endpoint_requires_auth(test_case, method, url, payload=None):
    """Utilitaire : vérifie qu'un endpoint retourne 401 sans authentification."""
    client = APIClient()
    call = getattr(client, method)
    kwargs = {}
    if payload is not None:
        import json
        kwargs = {"data": json.dumps(payload), "content_type": "application/json"}
    response = call(url, **kwargs)
    test_case.assertEqual(
        response.status_code,
        status.HTTP_401_UNAUTHORIZED,
        msg=f"{method.upper()} {url} devrait retourner 401 sans auth, reçu {response.status_code}",
    )


# ---------------------------------------------------------------------------
# Speciality
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class SpecialityEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints Speciality."""

    def setUp(self):
        self.client = APIClient()
        self.list_url = reverse("School:speciality_list_create")
        self.user = create_user()

    def test_get_specialities_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_speciality_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"name": "Piano"}
        )

    def test_get_speciality_detail_sans_auth_retourne_401(self):
        url = reverse("School:speciality_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "get", url)

    def test_put_speciality_sans_auth_retourne_401(self):
        url = reverse("School:speciality_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "put", url, payload={"name": "Violon"})

    def test_delete_speciality_sans_auth_retourne_401(self):
        url = reverse("School:speciality_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "delete", url)

    def test_get_specialities_avec_auth_retourne_200(self):
        """GET /School/specialities avec token valide doit retourner 200."""
        token = get_jwt_token(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token}")
        response = self.client.get(self.list_url, {"establishment_id": 1})
        self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST])


# ---------------------------------------------------------------------------
# Teacher
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class TeacherEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints Teacher."""

    def setUp(self):
        self.list_url = reverse("School:teacher_list_create")

    def test_get_teachers_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_teacher_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"first_name": "Jean"}
        )

    def test_get_teacher_detail_sans_auth_retourne_401(self):
        url = reverse("School:teacher_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "get", url)

    def test_put_teacher_sans_auth_retourne_401(self):
        url = reverse("School:teacher_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "put", url, payload={"first_name": "Pierre"})

    def test_delete_teacher_sans_auth_retourne_401(self):
        url = reverse("School:teacher_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "delete", url)


# ---------------------------------------------------------------------------
# SchoolClass
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class SchoolClassEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints SchoolClass."""

    def setUp(self):
        self.list_url = reverse("School:school_class_list_create")

    def test_get_school_classes_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_school_class_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"name": "Classe A"}
        )

    def test_get_school_class_detail_sans_auth_retourne_401(self):
        url = reverse("School:school_class_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "get", url)


# ---------------------------------------------------------------------------
# Fee
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class FeeEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints Fee."""

    def setUp(self):
        self.list_url = reverse("School:fee_list_create")

    def test_get_fees_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_fee_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"amount": 100}
        )

    def test_get_fee_detail_sans_auth_retourne_401(self):
        url = reverse("School:fee_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "get", url)

    def test_put_fee_sans_auth_retourne_401(self):
        url = reverse("School:fee_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "put", url, payload={"amount": 200})

    def test_delete_fee_sans_auth_retourne_401(self):
        url = reverse("School:fee_detail", kwargs={"id": 1})
        _assert_endpoint_requires_auth(self, "delete", url)


# ---------------------------------------------------------------------------
# Discount
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class DiscountEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints Discount."""

    def setUp(self):
        self.list_url = reverse("School:discount_list_create")

    def test_get_discounts_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_discount_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"rate": 10}
        )


# ---------------------------------------------------------------------------
# PaymentPlan
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class PaymentPlanEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints PaymentPlan."""

    def setUp(self):
        self.list_url = reverse("School:payment_plan_list_create")

    def test_get_payment_plans_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_payment_plan_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"name": "Plan A"}
        )


# ---------------------------------------------------------------------------
# PaymentMode
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class PaymentModeEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints PaymentMode."""

    def setUp(self):
        self.list_url = reverse("School:payment_mode_list_create")

    def test_get_payment_modes_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_payment_mode_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"name": "Virement"}
        )


# ---------------------------------------------------------------------------
# Competency
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class CompetencyEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints Competency."""

    def setUp(self):
        self.list_url = reverse("School:competency_list_create")

    def test_get_competencies_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_competency_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"name": "Lecture"}
        )


# ---------------------------------------------------------------------------
# EstablishmentCompetency
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class EstablishmentCompetencyEndpointAuthTest(TestCase):
    """Tests d'authentification sur les endpoints EstablishmentCompetency."""

    def setUp(self):
        self.list_url = reverse("School:establishment_competency_list_create")

    def test_get_establishment_competencies_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(self, "get", self.list_url)

    def test_post_establishment_competency_sans_auth_retourne_401(self):
        _assert_endpoint_requires_auth(
            self, "post", self.list_url, payload={"competency": 1}
        )


# ---------------------------------------------------------------------------
# Fee - validation du paramètre filter
# ---------------------------------------------------------------------------

@override_settings(**OVERRIDE_SETTINGS)
class FeeFilterValidationTest(TestCase):
    """Tests de validation du paramètre 'filter' sur l'endpoint Fee list."""

    def setUp(self):
        self.client = APIClient()
        self.list_url = reverse("School:fee_list_create")
        self.user = create_user("fee_filter_test@example.com")
        token = get_jwt_token(self.user)
        self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token}")

    def test_get_fees_sans_filter_retourne_400(self):
        """GET sans paramètre 'filter' doit retourner 400."""
        response = self.client.get(self.list_url, {"establishment_id": 1})
        self.assertEqual(
            response.status_code,
            status.HTTP_400_BAD_REQUEST,
            msg="GET sans filter devrait retourner 400",
        )

    def test_get_fees_filter_invalide_retourne_400(self):
        """GET avec un filtre inconnu doit retourner 400."""
        response = self.client.get(
            self.list_url, {"establishment_id": 1, "filter": "unknown"}
        )
        self.assertEqual(
            response.status_code,
            status.HTTP_400_BAD_REQUEST,
            msg="GET avec filter='unknown' devrait retourner 400",
        )

    def test_get_fees_filter_registration_accepte(self):
        """GET avec filter='registration' doit être accepté (200 ou 400 si establishment manquant)."""
        response = self.client.get(
            self.list_url, {"establishment_id": 99999, "filter": "registration"}
        )
        self.assertNotEqual(
            response.status_code,
            status.HTTP_400_BAD_REQUEST,
            msg="GET avec filter='registration' ne doit pas retourner 400 pour une raison de filtre invalide",
        )

    def test_get_fees_filter_tuition_accepte(self):
        """GET avec filter='tuition' doit être accepté (200 ou autre selon l'establishment)."""
        response = self.client.get(
            self.list_url, {"establishment_id": 99999, "filter": "tuition"}
        )
        self.assertNotEqual(
            response.status_code,
            status.HTTP_400_BAD_REQUEST,
            msg="GET avec filter='tuition' ne doit pas retourner 400 pour une raison de filtre invalide",
        )

    def test_get_fees_sans_establishment_id_retourne_400(self):
        """GET sans establishment_id doit retourner 400."""
        response = self.client.get(self.list_url, {"filter": "registration"})
        self.assertEqual(
            response.status_code,
            status.HTTP_400_BAD_REQUEST,
            msg="GET sans establishment_id devrait retourner 400",
        )
