"""Core + admin dashboard widgets.

Universal:    NotificationsWidget, ProfileCompletionBanner
Admin only:   PlatformKPIsWidget, AuditPulseWidget, UnverifiedUsersWidget,
              AdminQuickActions
"""
from __future__ import annotations

import json
from datetime import timedelta

from django.contrib.auth import get_user_model
from django.db.models import Count
from django.db.models.functions import TruncDate
from django.urls import reverse
from django.utils import timezone

from apps.core.audit.models import AuditLog
from apps.core.dashboard.base import (
    ZONE_ACTIONS,
    ZONE_KPI,
    ZONE_PRIMARY,
    ZONE_SIDE,
    DashboardWidget,
)
from apps.core.dashboard.registry import register
from apps.core.dashboard.role_groups import is_admin

User = get_user_model()


# Notifications are surfaced through the topnav bell on every page; we no
# longer render an unread-list widget on dashboards.


# ── Universal: profile completion nudge ──────────────────────────────────


@register
class ProfileCompletionBanner(DashboardWidget):
    """FRREP-UR006 — prompt any verified user whose professional profile
    (academic qualifications, professional background, areas of interest,
    country, biography) is incomplete to finish it.

    RIMS applicants already get a narrower, eligibility-driven version of
    this nudge (``rims.incomplete_profile_banner`` in ``rims_widgets.py``,
    gated on ``profile_gate.missing_profile_fields``) — skip this one for
    them so they don't see two competing "complete your profile" banners.
    """

    key = "core.profile_completion_banner"
    zone = ZONE_PRIMARY
    priority = 2
    template = "core/dashboard/widgets/banner_card.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        if not getattr(user, "is_authenticated", False):
            return False
        from apps.core.dashboard.widgets.rims_widgets import IncompleteProfileBanner

        if IncompleteProfileBanner.is_enabled(user):
            return False
        from apps.core.authentication.profile_completion import profile_completion

        return profile_completion(user)["percent"] < 100

    def get_context(self, user) -> dict:
        from apps.core.authentication.profile_completion import profile_completion

        data = profile_completion(user)
        missing = data["missing"]
        if not missing:
            return {}
        return {
            "title": "Complete your professional profile",
            "body": (
                f"{data['percent']}% complete — missing: " + ", ".join(missing) + "."
            ),
            "cta_label": "Complete profile",
            "cta_href": f"{reverse('accounts:profile')}?next={reverse('core:dashboard')}",
        }


# ── Admin: platform KPIs ─────────────────────────────────────────────────


# Admin KPIs render one card each — each metric is a small subclass.


class _AdminKPIBase(DashboardWidget):
    zone = ZONE_KPI
    template = "core/dashboard/widgets/kpi_card.html"
    # Name the admin KPI band so it doesn't inherit the RIMS-era "Research
    # Grants" fallback heading — these cards are platform-health metrics.
    zone_heading = "Platform health"
    zone_heading_url_name = "core:user_manage_list"
    zone_heading_action_label = "Manage users"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)


@register
class TotalUsersKPI(_AdminKPIBase):
    key = "core.kpi.users"
    priority = 11

    def get_context(self, user) -> dict:
        total = User.objects.count()
        last_30 = User.objects.filter(date_joined__gte=timezone.now() - timedelta(days=30)).count()
        return {
            "label": "Users",
            "value": f"{total:,}",
            "hint": f"+{last_30} in last 30 days",
            "href": reverse("core:user_manage_list"),
        }


@register
class UnverifiedUsersKPI(_AdminKPIBase):
    key = "core.kpi.unverified"
    priority = 12

    def get_context(self, user) -> dict:
        unverified = User.objects.filter(email_verified=False).count()
        return {
            "label": "Unverified emails",
            "value": f"{unverified:,}",
            "accent": "amber" if unverified > 0 else "primary",
            "hint": "Awaiting confirmation" if unverified else "All confirmed",
            "href": reverse("core:user_manage_list"),
        }


@register
class RepositoryDocsKPI(_AdminKPIBase):
    key = "core.kpi.repository"
    priority = 14

    def get_context(self, user) -> dict:
        from apps.repository.documents.models import Document

        total = Document.objects.count()
        published = Document.objects.filter(status=Document.Status.PUBLISHED).count()
        return {
            "label": "Repository documents",
            "value": f"{total:,}",
            "hint": f"{published:,} published",
            "href": reverse("repo_documents:browse"),
        }


@register
class AuditEventsTodayKPI(_AdminKPIBase):
    key = "core.kpi.audit_today"
    priority = 13

    @classmethod
    def is_enabled(cls, user) -> bool:
        # Audit telemetry lives on the dedicated audit log page; freeing the
        # KPI row keeps the admin dashboard at a tidy four cards.
        return False

    def get_context(self, user) -> dict:
        # 7-day sparkline of audit events per day, label = today's count.
        now = timezone.now()
        start = (now - timedelta(days=6)).replace(hour=0, minute=0, second=0, microsecond=0)
        rows = (
            AuditLog.objects.filter(timestamp__gte=start)
            .annotate(day=TruncDate("timestamp"))
            .values("day")
            .annotate(c=Count("id"))
            .order_by("day")
        )
        by_day = {str(r["day"]): r["c"] for r in rows}
        labels: list[str] = []
        values: list[int] = []
        for i in range(7):
            day = (start + timedelta(days=i)).date()
            labels.append(day.strftime("%a"))
            values.append(by_day.get(str(day), 0))
        today_count = values[-1] if values else 0
        return {
            "label": "Audit events today",
            "value": f"{today_count:,}",
            "hint": "Last 7 days",
            "href": reverse("core:audit_log_list"),
            "sparkline": {
                "labels_json": json.dumps(labels),
                "values_json": json.dumps(values),
            },
        }


# ── Admin side column: recently joined users ─────────────────────────────


@register
class RecentlyJoinedUsersWidget(DashboardWidget):
    key = "core.recent_users"
    zone = ZONE_SIDE
    priority = 50
    template = "core/dashboard/widgets/list_card.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)

    def get_context(self, user) -> dict:
        qs = (
            User.objects.select_related("institution")
            .order_by("-date_joined")[:6]
        )
        items = []
        for u in qs:
            label = u.get_full_name() or u.email
            inst = getattr(u.institution, "name", "") if getattr(u, "institution_id", None) else ""
            sublabel_bits = [u.role.replace("_", " ").title() if u.role else "—"]
            if inst:
                sublabel_bits.append(inst)
            badge_color = "emerald" if getattr(u, "email_verified", False) else "amber"
            items.append({
                "label": label,
                "sublabel": " · ".join(sublabel_bits),
                "badge": "Verified" if getattr(u, "email_verified", False) else "Unverified",
                "badge_color": badge_color,
                "href": reverse("core:user_manage_edit", args=[u.pk]),
            })
        return {
            "title": "Recently joined",
            "subtitle": "Newest accounts",
            "items": items,
            "empty_text": "No recent signups.",
            "footer_href": reverse("core:user_manage_list"),
            "footer_label": "All users",
        }


# ── Admin side column: top institutions ──────────────────────────────────


@register
class TopInstitutionsWidget(DashboardWidget):
    key = "core.top_institutions"
    zone = ZONE_SIDE
    priority = 55
    template = "core/dashboard/widgets/list_card.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)

    def get_context(self, user) -> dict:
        rows = (
            User.objects.exclude(institution__isnull=True)
            .values("institution__name", "institution__country")
            .annotate(c=Count("id"))
            .order_by("-c")[:5]
        )
        items = []
        for r in rows:
            country = r["institution__country"] or ""
            items.append({
                "label": r["institution__name"] or "—",
                "sublabel": country if country else "",
                "value": f"{r['c']:,}",
            })
        return {
            "title": "Top institutions",
            "subtitle": "By member count",
            "items": items,
            "empty_text": "No institutional members yet.",
        }


# ── Admin side column: attention-required cross-module queue ─────────────


@register
class AttentionRequiredWidget(DashboardWidget):
    """Single consolidated 'inbox' of cross-module pending items for admins."""

    key = "core.attention_required"
    zone = ZONE_SIDE
    priority = 45
    template = "core/dashboard/widgets/list_card.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)

    def get_context(self, user) -> dict:
        items: list = []

        # RIMS — applications waiting for review
        try:
            from apps.rims.grants.models import Application
            n = Application.objects.filter(status=Application.Status.SUBMITTED).count()
            if n:
                items.append({
                    "label": "Grant applications awaiting review",
                    "value": f"{n:,}",
                    "badge": "RIMS", "badge_color": "amber",
                    "href": reverse("rims_grants:manager_all_applications"),
                })
        except Exception:  # noqa: BLE001
            pass

        # Repository — QA queue + access requests
        try:
            from apps.repository.documents.models import AccessRequest, Document
            qa = Document.objects.filter(status=Document.Status.UNDER_QA).count()
            if qa:
                items.append({
                    "label": "Repository documents in QA",
                    "value": f"{qa:,}",
                    "badge": "Repository", "badge_color": "sky",
                    "href": reverse("repo_documents:qa_queue"),
                })
            ar = AccessRequest.objects.filter(status=AccessRequest.Status.PENDING).count()
            if ar:
                items.append({
                    "label": "Repository access requests",
                    "value": f"{ar:,}",
                    "badge": "Repository", "badge_color": "amber",
                    "href": reverse("repo_documents:access_request_queue"),
                })
        except Exception:  # noqa: BLE001
            pass

        # MEL — pending data points + activities at risk
        try:
            from apps.mel.indicators.models import DataPoint
            from apps.mel.tracking.models import Activity
            dp = DataPoint.objects.filter(status=DataPoint.Status.PENDING).count()
            if dp:
                items.append({
                    "label": "MEL data points to verify",
                    "value": f"{dp:,}",
                    "badge": "M&E", "badge_color": "amber",
                    "href": reverse("mel_indicators:data_entry"),
                })
            today = timezone.now().date()
            risk = Activity.objects.filter(scheduled_end__lt=today).exclude(status="completed").count()
            if risk:
                items.append({
                    "label": "MEL activities at risk",
                    "value": f"{risk:,}",
                    "badge": "M&E", "badge_color": "rose",
                    "href": reverse("mel_indicators:logframe_list"),
                })
        except Exception:  # noqa: BLE001
            pass

        # SME-Hub verifications
        try:
            from apps.smehub.onboarding.models import Business, EntrepreneurProfile
            ep = EntrepreneurProfile.objects.filter(verification_status="pending_verification").count()
            bp = Business.objects.filter(verification_status="pending_admin_review").count()
            if ep:
                items.append({
                    "label": "Entrepreneurs pending verification",
                    "value": f"{ep:,}",
                    "badge": "SME-Hub", "badge_color": "amber",
                    "href": reverse("smehub_onboarding:entrepreneur_queue"),
                })
            if bp:
                items.append({
                    "label": "Businesses pending review",
                    "value": f"{bp:,}",
                    "badge": "SME-Hub", "badge_color": "amber",
                    "href": reverse("smehub_onboarding:business_queue"),
                })
        except Exception:  # noqa: BLE001
            pass

        # Alumni mentorships pending
        try:
            from apps.alumni.engagement.models import MentorshipPairing, MentorshipStatus
            mp = MentorshipPairing.objects.filter(status=MentorshipStatus.REQUESTED).count()
            if mp:
                items.append({
                    "label": "Alumni mentorship requests",
                    "value": f"{mp:,}",
                    "badge": "Alumni", "badge_color": "amber",
                    "href": reverse("alumni_engagement:mentorship_list"),
                })
        except Exception:  # noqa: BLE001
            pass

        return {
            "title": "Attention required",
            "subtitle": f"{len(items)} item{'s' if len(items) != 1 else ''} across modules" if items else "Nothing needs attention",
            "items": items,
            "empty_text": "Inbox clear — no pending items across the platform.",
        }


# ── Admin: cross-module platform overview ────────────────────────────────


@register
class PlatformOverviewWidget(DashboardWidget):
    """One-glance snapshot of every major module — admin only."""

    key = "core.platform_overview"
    zone = ZONE_PRIMARY
    priority = 5
    template = "core/dashboard/widgets/list_card.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)

    def get_context(self, user) -> dict:
        items: list = []

        # RIMS — Grants
        try:
            from apps.rims.grants.models import Application, Award, GrantCall
            calls = GrantCall.objects.count()
            published = GrantCall.objects.filter(status=GrantCall.Status.PUBLISHED).count()
            apps = Application.objects.count()
            awards = Award.objects.filter(status=Award.Status.ACTIVE).count()
            items.append({
                "label": "RIMS — Grants",
                "sublabel": f"{calls} calls · {published} published · {apps} applications · {awards} active awards",
                "badge": "Live",
                "badge_color": "emerald",
                "href": reverse("rims_grants:manager_call_list"),
            })
        except Exception:  # noqa: BLE001
            pass

        # Repository
        try:
            from apps.repository.documents.models import AccessRequest, Document
            total_docs = Document.objects.count()
            published_docs = Document.objects.filter(status=Document.Status.PUBLISHED).count()
            qa = Document.objects.filter(status=Document.Status.UNDER_QA).count()
            access_pending = AccessRequest.objects.filter(status=AccessRequest.Status.PENDING).count()
            items.append({
                "label": "Repository",
                "sublabel": f"{total_docs} documents · {published_docs} published · {qa} in QA · {access_pending} access requests",
                "badge": "Live",
                "badge_color": "emerald",
                "href": reverse("repo_documents:browse"),
            })
        except Exception:  # noqa: BLE001
            pass

        # MEL
        try:
            from apps.mel.indicators.models import DataPoint, Indicator
            from apps.mel.tracking.models import Activity
            ind = Indicator.objects.filter(is_active=True).count()
            pending = DataPoint.objects.filter(status=DataPoint.Status.PENDING).count()
            today = timezone.now().date()
            at_risk = Activity.objects.filter(scheduled_end__lt=today).exclude(status="completed").count()
            items.append({
                "label": "M&E",
                "sublabel": f"{ind} active indicators · {pending} pending data points · {at_risk} activities at risk",
                "badge": "Live",
                "badge_color": "emerald",
                "href": reverse("mel_indicators:indicator_list"),
            })
        except Exception:  # noqa: BLE001
            pass

        # SME-Hub
        try:
            from apps.smehub.onboarding.models import Business, EntrepreneurProfile
            from apps.smehub.investment.models import FundingApplication
            ent_pending = EntrepreneurProfile.objects.filter(verification_status="pending_verification").count()
            biz_verified = Business.objects.filter(verification_status="verified").count()
            funding_apps = FundingApplication.objects.exclude(status="withdrawn").count()
            items.append({
                "label": "SME-Hub",
                "sublabel": f"{biz_verified} verified businesses · {ent_pending} entrepreneurs pending · {funding_apps} funding applications",
                "badge": "Live",
                "badge_color": "emerald",
                "href": reverse("smehub_onboarding:entrepreneur_queue"),
            })
        except Exception:  # noqa: BLE001
            pass

        # Alumni
        try:
            from apps.alumni.engagement.models import AlumniEvent
            from apps.alumni.profiles.models import AlumniProfile
            profiles = AlumniProfile.objects.count()
            events = AlumniEvent.objects.filter(start_at__gte=timezone.now()).count()
            items.append({
                "label": "Alumni",
                "sublabel": f"{profiles} profiles · {events} upcoming events",
                "badge": "Live",
                "badge_color": "emerald",
                "href": reverse("alumni_profiles:directory"),
            })
        except Exception:  # noqa: BLE001
            pass

        return {
            "title": "Platform overview",
            "subtitle": "Module-by-module snapshot",
            "items": items,
            "empty_text": "No module data available yet.",
        }


# ── Admin: quick actions ─────────────────────────────────────────────────


@register
class AdminQuickActions(DashboardWidget):
    key = "core.admin_actions"
    zone = ZONE_ACTIONS
    priority = 10
    template = "core/dashboard/widgets/quick_actions.html"

    @classmethod
    def is_enabled(cls, user) -> bool:
        return is_admin(user)

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "Manage users",
                    "href": reverse("core:user_manage_list"),
                    "primary": True,
                    "icon": "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z",
                },
                {
                    "label": "Roles & permissions",
                    "href": reverse("core:role_permissions_list"),
                    "icon": "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z",
                },
                {
                    "label": "Audit log",
                    "href": reverse("core:audit_log_list"),
                    "icon": "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2",
                },
            ],
        }
