"""Smoke tests for the personalized dashboard widget registry."""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model
from django.test import RequestFactory

from apps.core.dashboard.base import ZONE_ORDER, DashboardWidget
from apps.core.dashboard.registry import all_widgets, for_user
from apps.core.permissions.roles import UserRole

User = get_user_model()
pytestmark = pytest.mark.django_db


def _make_user(role: str = "", *, is_superuser: bool = False, email: str | None = None):
    email = email or f"{role or 'su'}@dashtest.local"
    return User.objects.create_user(
        email=email,
        password="testpw1234",
        first_name="Test",
        last_name="User",
        role=role,
        is_superuser=is_superuser,
        is_staff=is_superuser,
        is_active=True,
    )


def test_registry_loaded():
    """All widget modules import successfully and register at least 50 widgets."""
    widgets = all_widgets()
    assert len(widgets) >= 50, f"Expected ≥50 widgets, got {len(widgets)}"


def test_every_widget_has_required_attributes():
    for cls in all_widgets():
        assert cls.key, f"{cls.__name__} missing key"
        assert cls.zone in ZONE_ORDER, f"{cls.__name__} bad zone {cls.zone!r}"
        assert cls.template, f"{cls.__name__} missing template"
        assert callable(cls.is_enabled)


def test_anonymous_user_gets_no_widgets():
    class Anon:
        is_authenticated = False
        is_superuser = False
        role = ""

    assert for_user(Anon()) == []


@pytest.mark.parametrize(
    "role,must_have_keys",
    [
        (UserRole.APPLICANT, ["rims.my_applications", "rims.applicant_actions"]),
        # LEARNER / INSTRUCTOR REP dashboard widgets were removed with the REP LMS
        # (learning moved to Moodle); those roles have no dedicated widgets now.
        (UserRole.MEL_OFFICER, ["mel.actions", "mel.pending_data_points"]),
        (UserRole.ENTREPRENEUR, ["sme.entrepreneur_actions", "sme.my_business_banner"]),
        (UserRole.INVESTOR, ["sme.investor_actions"]),
        (UserRole.MENTOR, ["sme.mentor_assignments"]),
        (UserRole.ALUMNI, ["alumni.actions", "alumni.profile_banner"]),
        (UserRole.ALUMNI_OFFICER, ["alumni.officer_actions"]),
        (UserRole.REPO_MANAGER, ["repo.manager_actions", "repo.qa_queue"]),
    ],
)
def test_role_sees_expected_widgets(role, must_have_keys):
    user = _make_user(role)
    keys = {w.key for w in for_user(user)}
    for k in must_have_keys:
        assert k in keys, f"role={role} missing widget {k}; got {sorted(keys)}"


def test_admin_sees_admin_widgets_and_no_persona_creep():
    user = _make_user(UserRole.ADMIN)
    keys = {w.key for w in for_user(user)}
    # Has admin-specific widgets
    assert "core.admin_actions" in keys
    assert "core.platform_overview" in keys
    assert "core.attention_required" in keys
    # Does NOT spill into persona-only widgets
    assert "rep.my_enrolments" not in keys
    assert "alumni.my_mentorships" not in keys
    assert "sme.my_business_banner" not in keys


def test_applicant_does_not_see_admin_or_management_widgets():
    user = _make_user(UserRole.APPLICANT)
    keys = {w.key for w in for_user(user)}
    assert "core.admin_actions" not in keys
    assert "rims.review_queue" not in keys
    assert "rims.manager_actions" not in keys


def test_get_context_returns_dict_for_every_enabled_widget():
    """Every widget the registry hands back must produce a renderable dict."""
    for role in [UserRole.ADMIN, UserRole.GRANTS_MANAGER, UserRole.LEARNER, UserRole.ENTREPRENEUR]:
        user = _make_user(role, email=f"ctx-{role}@dashtest.local")
        for w in for_user(user):
            ctx = w.get_context(user)
            assert isinstance(ctx, dict), f"{w.key}.get_context did not return dict (role={role})"


def test_dashboard_view_renders_for_all_personas():
    """End-to-end: hitting /dashboard/ returns 200 for one user per role."""
    from django.test import Client
    from django.conf import settings

    from apps.core.authentication.models import MFADevice

    settings.ALLOWED_HOSTS = list(set([*settings.ALLOWED_HOSTS, "testserver"]))
    for role in [UserRole.ADMIN, UserRole.LEARNER, UserRole.ENTREPRENEUR, UserRole.MENTOR, UserRole.ALUMNI]:
        user = _make_user(role, email=f"e2e-{role}@dashtest.local")
        # Privileged roles trigger the MFA mandate; give each one a verified
        # TOTP device so the dashboard renders normally.
        MFADevice.objects.create(
            user=user,
            device_type=MFADevice.DeviceType.TOTP,
            name="test",
            secret="JBSWY3DPEHPK3PXP",
            is_verified=True,
        )
        client = Client()
        client.force_login(user)
        resp = client.get("/dashboard/")
        assert resp.status_code == 200, f"role={role} got {resp.status_code}"
        # No leaked Django template comments (multi-line {# #} bug regression).
        assert b"{#" not in resp.content
        assert b"#}" not in resp.content
