"""SME-Hub dashboard widgets — entrepreneurs, investors, mentors, admins."""
from __future__ import annotations

from django.urls import reverse
from django.utils import timezone

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 (
    ENTREPRENEUR_ROLES,
    INVESTOR_ROLES,
    MENTOR_ROLES,
    SME_ADMIN_ROLES,
    has_role,
)
from apps.core.permissions.roles import UserRole

BUYER_ROLES = (UserRole.BUYER,)
SERVICE_PROVIDER_ROLES = (UserRole.SERVICE_PROVIDER,)
PARTNER_ROLES = (UserRole.PARTNER,)
JUDGE_ROLES = (UserRole.JUDGE,)


# ── Helpers ──────────────────────────────────────────────────────────────


def _entrepreneur_for(user):
    try:
        from apps.smehub.onboarding.models import EntrepreneurProfile
        return EntrepreneurProfile.objects.filter(user=user).first()
    except Exception:  # noqa: BLE001
        return None


def _businesses_for(user):
    try:
        from apps.smehub.onboarding.models import Business
        ent = _entrepreneur_for(user)
        if not ent:
            return None
        return Business.objects.filter(entrepreneur=ent)
    except Exception:  # noqa: BLE001
        return None


# ── Entrepreneur: business profile banner ────────────────────────────────


@register
class MyBusinessBanner(DashboardWidget):
    key = "sme.my_business_banner"
    zone = ZONE_PRIMARY
    priority = 5
    template = "core/dashboard/widgets/banner_card.html"

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

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        if ent is None:
            return {
                "title": "Set up your entrepreneur profile",
                "body": "Complete your profile to apply to programmes and access investors.",
                "cta_label": "Start onboarding",
                "cta_href": reverse("smehub_onboarding:entrepreneur_profile_wizard"),
                "accent": "amber",
            }
        if ent.verification_status != "verified":
            return {
                "title": "Profile awaiting verification",
                "body": "An administrator is reviewing your profile. You'll be notified when verification is complete.",
                "cta_label": "View profile",
                "cta_href": reverse("smehub_onboarding:entrepreneur_profile_wizard"),
                "accent": "amber",
            }
        # Verified: prompt to register a business if none yet.
        businesses = _businesses_for(user) or []
        if not businesses:
            return {
                "title": "Register your business",
                "body": "Add your business to apply to incubation programmes and unlock investor matching.",
                "cta_label": "Register business",
                "cta_href": reverse("smehub_onboarding:business_register"),
                "accent": "emerald",
            }
        return {
            "title": f"Welcome back to SME-Hub",
            "body": "Manage your business, applications, and investor connections.",
            "cta_label": "My Dashboard",
            "cta_href": reverse("smehub_onboarding:my_dashboard"),
        }


# ── Entrepreneur KPIs ────────────────────────────────────────────────────


class _EntrepreneurKPIBase(DashboardWidget):
    zone = ZONE_KPI
    template = "core/dashboard/widgets/kpi_card.html"

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


@register
class EntrepreneurBusinessesKPI(_EntrepreneurKPIBase):
    key = "sme.kpi.businesses"
    priority = 80

    def get_context(self, user) -> dict:
        biz = _businesses_for(user)
        n = biz.count() if biz is not None else 0
        verified = biz.filter(verification_status="verified").count() if biz is not None else 0
        return {
            "label": "My businesses",
            "value": f"{n:,}",
            "hint": f"{verified} verified",
            "href": reverse("smehub_onboarding:my_businesses"),
        }


@register
class EntrepreneurApplicationsKPI(_EntrepreneurKPIBase):
    key = "sme.kpi.applications"
    priority = 81

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        if ent is None:
            return {"label": "Programme applications", "value": "0"}
        try:
            from apps.smehub.incubation.models import Application as IncApp
            n = IncApp.objects.filter(entrepreneur=ent).exclude(status="withdrawn").count()
            active = IncApp.objects.filter(entrepreneur=ent, status__in=["submitted", "under_review", "returned_for_info"]).count()
        except Exception:  # noqa: BLE001
            n = active = 0
        return {
            "label": "Programme applications",
            "value": f"{n:,}",
            "hint": f"{active} in progress",
        }


@register
class EntrepreneurFundingKPI(_EntrepreneurKPIBase):
    key = "sme.kpi.funding"
    priority = 82

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        if ent is None:
            return {"label": "Funding applications", "value": "0"}
        try:
            from apps.smehub.investment.models import FundingApplication
            n = FundingApplication.objects.filter(entrepreneur=ent).exclude(status="withdrawn").count()
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Funding applications",
            "value": f"{n:,}",
            "hint": "Submitted to investors",
        }


@register
class EntrepreneurMatchesKPI(_EntrepreneurKPIBase):
    key = "sme.kpi.matches"
    priority = 83

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        if ent is None:
            return {"label": "Investor matches", "value": "0"}
        try:
            from apps.smehub.investment.models import InvestorMatch
            n = InvestorMatch.objects.filter(entrepreneur=ent).count()
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Investor matches",
            "value": f"{n:,}",
            "accent": "emerald" if n else "primary",
            "hint": "AI-recommended investors" if n else "Verify business to unlock",
        }


# ── Entrepreneur: incubation progress list ───────────────────────────────


@register
class IncubationProgressWidget(DashboardWidget):
    key = "sme.incubation_progress"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        items: list = []
        if ent is not None:
            try:
                from apps.smehub.incubation.models import Application as IncApp
                qs = (
                    IncApp.objects.filter(entrepreneur=ent)
                    .exclude(status="withdrawn")
                    .select_related("programme", "business")
                    .order_by("-created_at")[:5]
                )
                color_map = {
                    "draft": "neutral",
                    "submitted": "amber",
                    "under_review": "sky",
                    "returned_for_info": "rose",
                    "accepted": "emerald",
                    "rejected": "rose",
                }
                for app in qs:
                    items.append({
                        "label": app.programme.title,
                        "sublabel": f"{app.business.name} • {app.application_id}",
                        "badge": app.get_status_display(),
                        "badge_color": color_map.get(app.status, "neutral"),
                    })
            except Exception:  # noqa: BLE001
                pass
        return {
            "title": "Incubation applications",
            "items": items,
            "empty_text": "You haven't applied to a programme yet.",
            "footer_href": reverse("smehub_incubation:programme_list"),
            "footer_label": "Browse programmes",
        }


# ── Entrepreneur: funding pipeline list ──────────────────────────────────


@register
class FundingPipelineWidget(DashboardWidget):
    key = "sme.funding_pipeline"
    zone = ZONE_PRIMARY
    priority = 85
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        items: list = []
        if ent is not None:
            try:
                from apps.smehub.investment.models import FundingApplication
                qs = (
                    FundingApplication.objects.filter(entrepreneur=ent)
                    .exclude(status="withdrawn")
                    .select_related("funding_call", "business")
                    .order_by("-id")[:5]
                )
                color_map = {
                    "draft": "neutral",
                    "submitted": "amber",
                    "under_review": "sky",
                    "returned_for_info": "rose",
                    "accepted": "emerald",
                    "rejected": "rose",
                }
                for app in qs:
                    items.append({
                        "label": app.funding_call.title if app.funding_call else "Funding application",
                        "sublabel": f"{app.business.name} • {app.application_id}",
                        "badge": app.get_status_display(),
                        "badge_color": color_map.get(app.status, "neutral"),
                    })
            except Exception:  # noqa: BLE001
                pass
        return {
            "title": "Funding pipeline",
            "items": items,
            "empty_text": "No funding applications yet.",
            "footer_href": reverse("smehub_investment:funding_calls"),
            "footer_label": "Browse funding calls",
        }


# ── Entrepreneur: top investor matches ───────────────────────────────────


@register
class InvestorMatchesWidget(DashboardWidget):
    key = "sme.investor_matches"
    zone = ZONE_SIDE
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        items: list = []
        if ent is not None:
            try:
                from apps.smehub.investment.models import InvestorMatch
                qs = (
                    InvestorMatch.objects.filter(entrepreneur=ent)
                    .select_related("investor")
                    .order_by("-score")[:5]
                )
                for m in qs:
                    score_pct = int(float(m.score) * 100) if m.score else 0
                    items.append({
                        "label": m.investor.org_name if hasattr(m.investor, "org_name") else str(m.investor),
                        "sublabel": f"Match score {score_pct}%",
                        "value": f"{score_pct}%",
                        "href": reverse("smehub_investment:investor_detail", args=[m.investor_id]),
                    })
            except Exception:  # noqa: BLE001
                pass
        return {
            "title": "Top investor matches",
            "items": items,
            "empty_text": "Verify your business to unlock investor matches.",
            "footer_href": reverse("smehub_investment:investor_directory"),
            "footer_label": "Browse investors",
        }


# ── Entrepreneur: marketplace summary ────────────────────────────────────


@register
class MarketplaceWidget(DashboardWidget):
    key = "sme.marketplace"
    zone = ZONE_SIDE
    priority = 85
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        ent = _entrepreneur_for(user)
        items: list = []
        if ent is not None:
            try:
                from apps.smehub.marketplace.models import ProductListing
                qs = (
                    ProductListing.objects.filter(business__entrepreneur=ent)
                    .order_by("-id")[:4]
                )
                for p in qs:
                    items.append({
                        "label": p.name if hasattr(p, "name") else "Product",
                        "sublabel": getattr(p, "category", "") or "",
                        "badge": p.get_status_display() if hasattr(p, "get_status_display") else None,
                        "badge_color": "emerald",
                    })
            except Exception:  # noqa: BLE001
                pass
        return {
            "title": "Marketplace",
            "subtitle": "Your products & demand responses",
            "items": items,
            "empty_text": "List a product to reach buyers.",
            "footer_href": reverse("smehub_marketplace:my_products"),
            "footer_label": "My products",
        }


# ── Entrepreneur quick actions ───────────────────────────────────────────


@register
class EntrepreneurQuickActions(DashboardWidget):
    key = "sme.entrepreneur_actions"
    zone = ZONE_ACTIONS
    priority = 60
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {"label": "Browse programmes", "href": reverse("smehub_incubation:programme_list"), "primary": True, "icon": "M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2H7a2 2 0 00-2 2v2"},
                {
                    "label": "Funding calls",
                    "href": reverse("smehub_investment:funding_calls"),
                    "icon": "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
                },
                {
                    "label": "List a product",
                    "href": reverse("smehub_marketplace:product_create"),
                    "icon": "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z",
                },
                {
                    "label": "My businesses",
                    "href": reverse("smehub_onboarding:my_businesses"),
                    "icon": "M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z",
                },
            ],
        }


# ── Investor KPIs ────────────────────────────────────────────────────────


class _InvestorKPIBase(DashboardWidget):
    zone = ZONE_KPI
    template = "core/dashboard/widgets/kpi_card.html"

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


@register
class InvestorMatchesKPI(_InvestorKPIBase):
    key = "sme.kpi.inv_matches"
    priority = 90

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.investment.models import Investor, InvestorMatch
            inv = Investor.objects.filter(account=user).first() if hasattr(Investor, "account") else None
            n = InvestorMatch.objects.filter(investor=inv).count() if inv else 0
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Suggested matches",
            "value": f"{n:,}",
            "hint": "Entrepreneurs to consider",
            "href": reverse("smehub_investment:investor_directory"),
        }


@register
class InvestorFundingCallsKPI(_InvestorKPIBase):
    key = "sme.kpi.inv_calls"
    priority = 91

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.investment.models import FundingCall
            n = FundingCall.objects.filter(status__in=["published", "open"]).count() if hasattr(FundingCall, "status") else FundingCall.objects.count()
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Open funding calls",
            "value": f"{n:,}",
            "hint": "Active across the platform",
            "href": reverse("smehub_investment:funding_calls"),
        }


@register
class InvestorApplicationsKPI(_InvestorKPIBase):
    key = "sme.kpi.inv_applications"
    priority = 92

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.investment.models import FundingApplication
            n = FundingApplication.objects.filter(status="submitted").count()
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Applications received",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "Awaiting your review" if n else "Inbox empty",
        }


@register
class InvestorQuickActions(DashboardWidget):
    key = "sme.investor_actions"
    zone = ZONE_ACTIONS
    priority = 70
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {"label": "Browse SMEs", "href": reverse("smehub_investment:investor_directory"), "primary": True, "icon": "M21 21l-4.35-4.35M11 18a7 7 0 100-14 7 7 0 000 14z"},
                {
                    "label": "Funding calls",
                    "href": reverse("smehub_investment:funding_calls"),
                    "icon": "M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z",
                },
                {
                    "label": "Manage calls",
                    "href": reverse("smehub_investment:funding_call_manage"),
                    "icon": "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z",
                },
            ],
        }


# ── Mentor: assignments ──────────────────────────────────────────────────


@register
class MentorAssignmentsWidget(DashboardWidget):
    key = "sme.mentor_assignments"
    zone = ZONE_PRIMARY
    priority = 90
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.incubation.models import MentorAssignment
            qs = (
                MentorAssignment.objects.filter(mentor=user, ended_at__isnull=True)
                .select_related("cohort_member__business", "cohort_member__cohort")[:6]
            )
            for ma in qs:
                cm = ma.cohort_member
                items.append({
                    "label": cm.business.name if cm and cm.business else "Mentee",
                    "sublabel": cm.cohort.name if cm and cm.cohort else "",
                    "badge": "Active",
                    "badge_color": "emerald",
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Your mentees",
            "items": items,
            "empty_text": "No active mentor assignments.",
        }


@register
class MentorActiveKPI(DashboardWidget):
    key = "sme.kpi.mentor_active"
    zone = ZONE_KPI
    priority = 95
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.incubation.models import MentorAssignment
            n = MentorAssignment.objects.filter(mentor=user, ended_at__isnull=True).count()
        except Exception:  # noqa: BLE001
            n = 0
        return {"label": "Active mentees", "value": f"{n:,}", "hint": "Currently mentoring"}


@register
class MentorSessionsThisMonthKPI(DashboardWidget):
    key = "sme.kpi.mentor_sessions_month"
    zone = ZONE_KPI
    priority = 96
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.incubation.models import MentorSession
            from django.utils import timezone as tz
            now = tz.now()
            n = MentorSession.objects.filter(
                mentor_assignment__mentor=user,
                scheduled_at__year=now.year,
                scheduled_at__month=now.month,
            ).count() if hasattr(MentorSession, "scheduled_at") else MentorSession.objects.filter(mentor_assignment__mentor=user).count()
        except Exception:  # noqa: BLE001
            n = 0
        return {"label": "Sessions this month", "value": f"{n:,}", "hint": "Mentor sessions logged"}


@register
class MentorTotalSessionsKPI(DashboardWidget):
    key = "sme.kpi.mentor_total_sessions"
    zone = ZONE_KPI
    priority = 97
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.incubation.models import MentorSession
            n = MentorSession.objects.filter(mentor_assignment__mentor=user).count()
        except Exception:  # noqa: BLE001
            n = 0
        return {"label": "Total sessions", "value": f"{n:,}", "hint": "All-time"}


@register
class MentorUpcomingSessionsWidget(DashboardWidget):
    key = "sme.mentor_upcoming_sessions"
    zone = ZONE_SIDE
    priority = 90
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.incubation.models import MentorSession
            from django.utils import timezone as tz
            qs_attr = "scheduled_at" if hasattr(MentorSession, "scheduled_at") else "created_at"
            qs = (
                MentorSession.objects.filter(mentor_assignment__mentor=user)
                .filter(**{f"{qs_attr}__gte": tz.now()})
                .select_related("mentor_assignment__cohort_member__business")
                .order_by(qs_attr)[:5]
            )
            for s in qs:
                ts = getattr(s, qs_attr, None)
                cm = getattr(s.mentor_assignment, "cohort_member", None)
                biz = getattr(cm, "business", None) if cm else None
                items.append({
                    "label": biz.name if biz else "Mentor session",
                    "sublabel": ts.strftime("%b %d, %H:%M") if ts else "",
                    "badge": "Upcoming",
                    "badge_color": "sky",
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Upcoming sessions",
            "items": items,
            "empty_text": "No upcoming sessions scheduled.",
        }


@register
class MentorQuickActions(DashboardWidget):
    key = "sme.mentor_actions"
    zone = ZONE_ACTIONS
    priority = 72
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {"label": "Mentor workspace", "href": reverse("smehub_incubation:mentor_dashboard"), "primary": True, "icon": "M3.75 6A2.25 2.25 0 016 3.75h2.25A2.25 2.25 0 0110.5 6v2.25a2.25 2.25 0 01-2.25 2.25H6a2.25 2.25 0 01-2.25-2.25V6zM3.75 15.75A2.25 2.25 0 016 13.5h2.25a2.25 2.25 0 012.25 2.25V18a2.25 2.25 0 01-2.25 2.25H6A2.25 2.25 0 013.75 18v-2.25zM13.5 6a2.25 2.25 0 012.25-2.25H18A2.25 2.25 0 0120.25 6v2.25A2.25 2.25 0 0118 10.5h-2.25a2.25 2.25 0 01-2.25-2.25V6zM13.5 15.75a2.25 2.25 0 012.25-2.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-2.25A2.25 2.25 0 0113.5 18v-2.25z"},
                {"label": "My mentees", "href": reverse("smehub_incubation:cohort_list"), "icon": "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"},
                {"label": "Meeting requests", "href": reverse("smehub_onboarding:mentor_meeting_inbox"), "icon": "M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5"},
                {"label": "Mentor directory", "href": reverse("smehub_onboarding:mentor_directory"), "icon": "M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"},
            ],
        }


# ── Investor: recent applications received ───────────────────────────────


@register
class InvestorRecentApplicationsWidget(DashboardWidget):
    key = "sme.investor_recent_applications"
    zone = ZONE_PRIMARY
    priority = 92
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.investment.models import FundingApplication
            qs = (
                FundingApplication.objects.exclude(status="withdrawn")
                .select_related("funding_call", "business")
                .order_by("-id")[:8]
            )
            color_map = {
                "draft": "neutral",
                "submitted": "amber",
                "under_review": "sky",
                "returned_for_info": "rose",
                "accepted": "emerald",
                "rejected": "rose",
            }
            for app in qs:
                items.append({
                    "label": app.business.name if app.business else "Application",
                    "sublabel": app.funding_call.title if app.funding_call else "",
                    "badge": app.get_status_display(),
                    "badge_color": color_map.get(app.status, "neutral"),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Recent applications",
            "subtitle": "Across all funding calls",
            "items": items,
            "empty_text": "No funding applications yet.",
            "footer_href": reverse("smehub_investment:funding_call_manage"),
            "footer_label": "Manage calls",
        }


# ── SME Admin: queues ────────────────────────────────────────────────────


@register
class SMEHubPipelineOverviewWidget(DashboardWidget):
    """One-card snapshot of every SME-Hub subprogramme — admin/officer only."""

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

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.onboarding.models import Business, EntrepreneurProfile
            ent_total = EntrepreneurProfile.objects.count()
            ent_verified = EntrepreneurProfile.objects.filter(verification_status="verified").count()
            biz_total = Business.objects.count()
            biz_verified = Business.objects.filter(verification_status="verified").count()
            items.append({
                "label": "SP1 — Onboarding",
                "sublabel": f"{ent_total} entrepreneurs ({ent_verified} verified) · {biz_total} businesses ({biz_verified} verified)",
                "badge": "Live", "badge_color": "emerald",
                "href": reverse("smehub_onboarding:entrepreneur_queue"),
            })
        except Exception:  # noqa: BLE001
            pass
        try:
            from apps.smehub.incubation.models import Application as IncApp, Cohort
            apps_total = IncApp.objects.exclude(status="withdrawn").count()
            apps_review = IncApp.objects.filter(status__in=["submitted", "under_review"]).count()
            cohorts = Cohort.objects.count()
            items.append({
                "label": "SP2 — Incubation",
                "sublabel": f"{apps_total} applications · {apps_review} in review · {cohorts} cohorts",
                "badge": "Live", "badge_color": "emerald",
                "href": reverse("smehub_incubation:programme_manage_list"),
            })
        except Exception:  # noqa: BLE001
            pass
        try:
            from apps.smehub.linkage.models import ConnectionRequest, MoU
            cr = ConnectionRequest.objects.count()
            mou = MoU.objects.count()
            items.append({
                "label": "SP3 — Linkage",
                "sublabel": f"{cr} connection requests · {mou} MoUs",
                "badge": "Live", "badge_color": "emerald",
                "href": reverse("smehub_linkage:partner_directory"),
            })
        except Exception:  # noqa: BLE001
            pass
        try:
            from apps.smehub.marketplace.models import MarketDemandListing, ProductListing
            products = ProductListing.objects.count()
            demands = MarketDemandListing.objects.count()
            items.append({
                "label": "SP3b — Marketplace",
                "sublabel": f"{products} products · {demands} demand listings",
                "badge": "Live", "badge_color": "emerald",
                "href": reverse("smehub_marketplace:product_catalogue"),
            })
        except Exception:  # noqa: BLE001
            pass
        try:
            from apps.smehub.investment.models import FundingApplication, FundingCall
            calls = FundingCall.objects.count()
            apps_inv = FundingApplication.objects.exclude(status="withdrawn").count()
            items.append({
                "label": "SP5 — Investment",
                "sublabel": f"{calls} funding calls · {apps_inv} applications",
                "badge": "Live", "badge_color": "emerald",
                "href": reverse("smehub_investment:admin_dashboard"),
            })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "SME-Hub pipeline",
            "subtitle": "Subprogramme snapshot",
            "items": items,
            "empty_text": "No SME-Hub data available yet.",
        }


@register
class SMEHubFundingApplicationsWidget(DashboardWidget):
    key = "sme.admin_recent_funding_apps"
    zone = ZONE_SIDE
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.investment.models import FundingApplication
            qs = (
                FundingApplication.objects.exclude(status="withdrawn")
                .select_related("business", "funding_call")
                .order_by("-id")[:5]
            )
            color_map = {
                "draft": "neutral",
                "submitted": "amber",
                "under_review": "sky",
                "accepted": "emerald",
                "rejected": "rose",
                "returned_for_info": "rose",
            }
            for app in qs:
                items.append({
                    "label": app.business.name if app.business else "Application",
                    "sublabel": app.funding_call.title if app.funding_call else "",
                    "badge": app.get_status_display(),
                    "badge_color": color_map.get(app.status, "neutral"),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Recent funding applications",
            "items": items,
            "empty_text": "No funding applications yet.",
            "footer_href": reverse("smehub_investment:admin_dashboard"),
            "footer_label": "Investment dashboard",
        }


@register
class SMEAdminQueueKPI(DashboardWidget):
    key = "sme.kpi.admin_pending"
    zone = ZONE_KPI
    priority = 100
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.onboarding.models import Business, EntrepreneurProfile
            ent_pending = EntrepreneurProfile.objects.filter(verification_status="pending_verification").count()
            biz_pending = Business.objects.filter(verification_status="pending_admin_review").count()
        except Exception:  # noqa: BLE001
            ent_pending = biz_pending = 0
        total = ent_pending + biz_pending
        return {
            "label": "SME-Hub verifications",
            "value": f"{total:,}",
            "accent": "amber" if total else "primary",
            "hint": f"{ent_pending} entrepreneurs · {biz_pending} businesses",
            "href": reverse("smehub_onboarding:entrepreneur_queue"),
        }


@register
class SMEAdminPendingEntrepreneursWidget(DashboardWidget):
    key = "sme.admin_entrepreneur_queue"
    zone = ZONE_PRIMARY
    priority = 100
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.onboarding.models import EntrepreneurProfile
            qs = (
                EntrepreneurProfile.objects.filter(verification_status="pending_verification")
                .select_related("user")
                .order_by("-created_at")[:5]
            )
            for ent in qs:
                items.append({
                    "label": ent.user.get_full_name() or ent.user.email,
                    "sublabel": ent.organisation or ent.country or "",
                    "badge": "Verify",
                    "badge_color": "amber",
                    "href": reverse("smehub_onboarding:entrepreneur_detail", args=[ent.pk]),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Entrepreneur verifications",
            "subtitle": "Pending admin review",
            "items": items,
            "empty_text": "No pending entrepreneur verifications.",
            "footer_href": reverse("smehub_onboarding:entrepreneur_queue"),
            "footer_label": "Open queue",
        }


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

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "Entrepreneur queue",
                    "href": reverse("smehub_onboarding:entrepreneur_queue"),
                    "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": "Business queue",
                    "href": reverse("smehub_onboarding:business_queue"),
                    "icon": "M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z",
                },
                {
                    "label": "Investment dashboard",
                    "href": reverse("smehub_investment:admin_dashboard"),
                    "icon": "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z",
                },
            ],
        }


# ─────────────────────────────────────────────────────────────────────────
# Buyer / Service Provider / Partner / Judge — peripheral SME-Hub roles
# ─────────────────────────────────────────────────────────────────────────


# ── Buyer ────────────────────────────────────────────────────────────────


@register
class BuyerOpenDemandsKPI(DashboardWidget):
    key = "sme.kpi.buyer_open_demands"
    zone = ZONE_KPI
    priority = 80
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.marketplace.models import MarketDemandListing
            mine = MarketDemandListing.objects.filter(created_by=user).count() if hasattr(MarketDemandListing, "created_by") else 0
        except Exception:  # noqa: BLE001
            mine = 0
        return {
            "label": "My demand listings",
            "value": f"{mine:,}",
            "hint": "Posted by you",
            "href": reverse("smehub_marketplace:my_demand_listings"),
        }


@register
class BuyerResponsesKPI(DashboardWidget):
    key = "sme.kpi.buyer_responses"
    zone = ZONE_KPI
    priority = 81
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.marketplace.models import DemandResponse
            n = DemandResponse.objects.filter(demand__created_by=user).count() if hasattr(DemandResponse, "demand") else 0
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Responses received",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "From SMEs",
        }


@register
class BuyerActiveProductsKPI(DashboardWidget):
    key = "sme.kpi.buyer_marketplace"
    zone = ZONE_KPI
    priority = 82
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.marketplace.models import ProductListing
            n = ProductListing.objects.filter(status="published").count() if hasattr(ProductListing, "status") else ProductListing.objects.count()
        except Exception:  # noqa: BLE001
            n = 0
        return {
            "label": "Products available",
            "value": f"{n:,}",
            "hint": "From verified SMEs",
            "href": reverse("smehub_marketplace:product_catalogue"),
        }


@register
class BuyerRecentResponsesWidget(DashboardWidget):
    key = "sme.buyer_recent_responses"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.marketplace.models import DemandResponse
            qs = (
                DemandResponse.objects.filter(demand__created_by=user)
                .select_related("demand", "business")
                .order_by("-id")[:6]
            )
            color_map = {
                "pending": "amber",
                "accepted": "emerald",
                "declined": "rose",
            }
            for r in qs:
                items.append({
                    "label": (r.business.name if r.business else "Response"),
                    "sublabel": r.demand.title if r.demand else "",
                    "badge": r.get_status_display() if hasattr(r, "get_status_display") else "Response",
                    "badge_color": color_map.get(getattr(r, "status", ""), "neutral"),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Latest responses",
            "subtitle": "SMEs responding to your demand listings",
            "items": items,
            "empty_text": "No responses received yet.",
        }


@register
class BuyerQuickActions(DashboardWidget):
    key = "sme.buyer_actions"
    zone = ZONE_ACTIONS
    priority = 70
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {"label": "Post a demand", "href": reverse("smehub_marketplace:demand_create"), "primary": True, "icon": "M12 4v16m8-8H4"},
                {
                    "label": "Browse products",
                    "href": reverse("smehub_marketplace:product_catalogue"),
                    "icon": "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z",
                },
                {
                    "label": "My demand listings",
                    "href": reverse("smehub_marketplace:my_demand_listings"),
                    "icon": "M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z",
                },
            ],
        }


# ── Service Provider ─────────────────────────────────────────────────────


@register
class ServiceProviderConnectionsKPI(DashboardWidget):
    key = "sme.kpi.service_connections"
    zone = ZONE_KPI
    priority = 80
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.linkage.models import ConnectionRequest
            from django.contrib.contenttypes.models import ContentType
            n = ConnectionRequest.objects.filter(status="accepted").count()
        except Exception:  # noqa: BLE001
            n = 0
        return {"label": "Connection requests", "value": f"{n:,}", "hint": "Across the SME-Hub"}


@register
class ServiceProviderOpenDemandsWidget(DashboardWidget):
    key = "sme.service_open_demands"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.marketplace.models import MarketDemandListing
            qs = MarketDemandListing.objects.order_by("-id")[:6]
            for d in qs:
                items.append({
                    "label": getattr(d, "title", "Demand"),
                    "sublabel": getattr(d, "category", "") or getattr(d, "sector", ""),
                    "badge": "Open",
                    "badge_color": "emerald",
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Open demand opportunities",
            "subtitle": "From buyers seeking services",
            "items": items,
            "empty_text": "No open demands at the moment.",
            "footer_href": reverse("smehub_marketplace:demand_list"),
            "footer_label": "Browse all demands",
        }


@register
class ServiceProviderQuickActions(DashboardWidget):
    key = "sme.service_actions"
    zone = ZONE_ACTIONS
    priority = 70
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "Browse demands",
                    "href": reverse("smehub_marketplace:demand_list"),
                    "primary": True,
                    "icon": "M21 21l-4.35-4.35M11 18a7 7 0 100-14 7 7 0 000 14z",
                },
                {
                    "label": "My responses",
                    "href": reverse("smehub_marketplace:my_demand_responses"),
                    "icon": "M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z",
                },
                {
                    "label": "Service provider directory",
                    "href": reverse("smehub_linkage:service_provider_list"),
                    "icon": "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z",
                },
            ],
        }


# ── Partner ──────────────────────────────────────────────────────────────


@register
class PartnerActiveMoUsKPI(DashboardWidget):
    key = "sme.kpi.partner_mous"
    zone = ZONE_KPI
    priority = 80
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        try:
            from apps.smehub.linkage.models import MoU
            n = MoU.objects.filter(status="formalised").count() if hasattr(MoU, "status") else MoU.objects.count()
        except Exception:  # noqa: BLE001
            n = 0
        return {"label": "Active MoUs", "value": f"{n:,}", "hint": "Formalised partnerships"}


@register
class PartnerRecentMoUsWidget(DashboardWidget):
    key = "sme.partner_recent_mous"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.linkage.models import MoU
            qs = MoU.objects.order_by("-id")[:6]
            color_map = {
                "draft": "neutral",
                "review": "sky",
                "approved": "amber",
                "formalised": "emerald",
                "expired": "rose",
            }
            for m in qs:
                items.append({
                    "label": getattr(m, "title", None) or f"MoU #{m.pk}",
                    "sublabel": "",
                    "badge": m.get_status_display() if hasattr(m, "get_status_display") else "MoU",
                    "badge_color": color_map.get(getattr(m, "status", ""), "neutral"),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Recent MoUs",
            "items": items,
            "empty_text": "No MoUs yet.",
            "footer_href": reverse("smehub_linkage:partner_directory"),
            "footer_label": "Partner directory",
        }


@register
class PartnerQuickActions(DashboardWidget):
    key = "sme.partner_actions"
    zone = ZONE_ACTIONS
    priority = 70
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "Partner directory",
                    "href": reverse("smehub_linkage:partner_directory"),
                    "primary": True,
                    "icon": "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z",
                },
                {
                    "label": "Post a job opportunity",
                    "href": reverse("alumni_jobs:submit"),
                    "icon": "M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z",
                },
                {
                    "label": "My job submissions",
                    "href": reverse("alumni_jobs:my_submissions"),
                    "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",
                },
            ],
        }


# ── Judge (already covered as REVIEWER_ROLES for RIMS — add SME-Hub queue)


@register
class JudgeSMEQueueWidget(DashboardWidget):
    key = "sme.judge_queue"
    zone = ZONE_PRIMARY
    priority = 75
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.incubation.models import Application as IncApp
            qs = (
                IncApp.objects.filter(
                    status="under_review",
                    judge_assignments__judge=user,
                )
                .select_related("programme", "business")
                .order_by("-submitted_at")[:6]
            )
            for app in qs:
                items.append({
                    "label": app.business.name if app.business else "Application",
                    "sublabel": app.programme.title if app.programme else "",
                    "badge": "To score",
                    "badge_color": "amber",
                    "href": reverse("smehub_incubation:judge_score_form", args=[app.pk]),
                })
        except Exception:  # noqa: BLE001
            pass
        try:
            from apps.smehub.showcasing.models import (
                InnovationChallenge,
                ShowcaseEvent,
            )
            for ev in ShowcaseEvent.objects.filter(evaluation_panel=user).exclude(
                status=ShowcaseEvent.Status.DRAFT,
            ).order_by("-starts_at")[:3]:
                items.append({
                    "label": ev.name,
                    "sublabel": "Showcase event · pitches to score",
                    "badge": "Panel",
                    "badge_color": "sky",
                    "href": reverse("smehub_showcasing:event_applications", args=[ev.pk]),
                })
            for ch in InnovationChallenge.objects.filter(evaluation_panel=user).exclude(
                status=InnovationChallenge.Status.DRAFT,
            ).order_by("-application_deadline")[:3]:
                items.append({
                    "label": ch.title,
                    "sublabel": "Innovation challenge · entries to score",
                    "badge": "Panel",
                    "badge_color": "sky",
                    "href": reverse("smehub_showcasing:challenge_applications", args=[ch.pk]),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "SME-Hub: applications to score",
            "subtitle": "Awaiting your scoring",
            "items": items,
            "empty_text": "No applications awaiting scoring.",
            "footer_href": reverse("smehub_incubation:judge_queue"),
            "footer_label": "Open judge queue",
        }


@register
class JudgeQuickActions(DashboardWidget):
    key = "sme.judge_actions"
    zone = ZONE_ACTIONS
    priority = 30
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "SME-Hub judge queue",
                    "href": reverse("smehub_incubation:judge_queue"),
                    "primary": True,
                    "icon": "M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z",
                },
            ],
        }


# ── Entrepreneur: upcoming showcasing events ─────────────────────────────


@register
class EntrepreneurUpcomingShowcasingWidget(DashboardWidget):
    key = "sme.entrepreneur_upcoming_showcasing"
    zone = ZONE_SIDE
    priority = 87
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.showcasing.models import (
                InnovationChallenge,
                ShowcaseEvent,
            )
            from django.db.models import Q

            now = timezone.now()
            events = (
                ShowcaseEvent.objects.filter(
                    Q(status=ShowcaseEvent.Status.LIVE)
                    | Q(
                        status=ShowcaseEvent.Status.PUBLISHED,
                        starts_at__gte=now,
                    )
                )
                .order_by("starts_at")[:3]
            )
            for ev in events:
                is_live = ev.status == ShowcaseEvent.Status.LIVE
                items.append({
                    "label": ev.name,
                    "sublabel": timezone.localtime(ev.starts_at).strftime("%b %d, %H:%M"),
                    "badge": "Live now" if is_live else "Event",
                    "badge_color": "red" if is_live else "sky",
                    "href": reverse("smehub_showcasing:event_detail", args=[ev.pk]),
                })
            challenges = (
                InnovationChallenge.objects.filter(
                    status=InnovationChallenge.Status.OPEN,
                    application_deadline__gte=now,
                )
                .order_by("application_deadline")[:2]
            )
            for ch in challenges:
                deadline = timezone.localtime(ch.application_deadline)
                items.append({
                    "label": ch.title,
                    "sublabel": f"Closes {deadline.strftime('%b %d')}",
                    "badge": "Challenge",
                    "badge_color": "amber",
                    "href": reverse("smehub_showcasing:challenge_detail", args=[ch.pk]),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Showcasing & challenges",
            "subtitle": "Open to entrepreneurs",
            "items": items,
            "empty_text": "No upcoming events or challenges.",
            "footer_href": reverse("smehub_showcasing:event_list"),
            "footer_label": "Browse showcasing",
        }


# ── Mentor: mentee readiness scores ──────────────────────────────────────


@register
class MentorMenteeReadinessWidget(DashboardWidget):
    key = "sme.mentor_mentee_readiness"
    zone = ZONE_PRIMARY
    priority = 92
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.incubation.models import (
                MentorAssignment,
                ReadinessAssessment,
            )
            assignments = (
                MentorAssignment.objects.filter(mentor=user, ended_at__isnull=True)
                .select_related("cohort_member__entrepreneur__user", "cohort_member__cohort")[:6]
            )
            for ma in assignments:
                ent = ma.cohort_member.entrepreneur
                latest = (
                    ReadinessAssessment.objects.filter(cohort_member=ma.cohort_member)
                    .order_by("-id")
                    .first()
                )
                if latest is not None:
                    avg = float(latest.average_score or 0)
                    score_label = f"{avg:.1f}/5"
                    color = "emerald" if avg >= 4 else ("amber" if avg >= 3 else "rose")
                else:
                    score_label = "—"
                    color = "neutral"
                items.append({
                    "label": ent.user.get_full_name() or ent.user.email,
                    "sublabel": ma.cohort_member.cohort.name if ma.cohort_member.cohort else "",
                    "badge": score_label,
                    "badge_color": color,
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Mentee readiness",
            "subtitle": "Latest assessment scores",
            "items": items,
            "empty_text": "No mentee readiness data yet.",
        }


# ── Investor: open deal rooms ────────────────────────────────────────────


@register
class InvestorOpenDealRoomsWidget(DashboardWidget):
    key = "sme.investor_open_deal_rooms"
    zone = ZONE_PRIMARY
    priority = 94
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.investment.models import Investor
            from apps.smehub.showcasing.models import DealRoom

            investor = Investor.objects.filter(contact_user=user).first()
            if investor is not None:
                ct = ContentType.objects.get_for_model(Investor)
                qs = (
                    DealRoom.objects.filter(
                        status=DealRoom.Status.OPEN,
                        expiry_date__gt=timezone.now(),
                        counterparty_content_type=ct,
                        counterparty_object_id=str(investor.pk),
                    )
                    .select_related("business")
                    .order_by("expiry_date")[:5]
                )
                for room in qs:
                    expires = timezone.localtime(room.expiry_date)
                    items.append({
                        "label": room.business.name if room.business else "Deal room",
                        "sublabel": f"Expires {expires.strftime('%b %d')}",
                        "badge": "Open",
                        "badge_color": "emerald",
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Open deal rooms",
            "subtitle": "Active negotiations",
            "items": items,
            "empty_text": "No open deal rooms yet.",
        }


# ── Investor: upcoming showcase events ──────────────────────────────────


@register
class InvestorUpcomingShowcasesWidget(DashboardWidget):
    key = "sme.investor_upcoming_showcases"
    zone = ZONE_SIDE
    priority = 88
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from django.db.models import Q

            from apps.smehub.showcasing.models import ShowcaseEvent
            now = timezone.now()
            qs = (
                ShowcaseEvent.objects.filter(
                    Q(status=ShowcaseEvent.Status.LIVE)
                    | Q(
                        status=ShowcaseEvent.Status.PUBLISHED,
                        starts_at__gte=now,
                    )
                )
                .order_by("starts_at")[:5]
            )
            for ev in qs:
                is_live = ev.status == ShowcaseEvent.Status.LIVE
                items.append({
                    "label": ev.name,
                    "sublabel": (
                        "Live now"
                        if is_live
                        else timezone.localtime(ev.starts_at).strftime("%a %b %d, %H:%M")
                    ),
                    "href": reverse("smehub_showcasing:event_detail", args=[ev.pk]),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Upcoming showcases",
            "subtitle": "Discover new SMEs",
            "items": items,
            "empty_text": "No upcoming events scheduled.",
            "footer_href": reverse("smehub_showcasing:event_list"),
            "footer_label": "Browse events",
        }


# ── Buyer: open demand listings + recent sales ──────────────────────────


@register
class BuyerOpenDemandsWidget(DashboardWidget):
    key = "sme.buyer_open_demands"
    zone = ZONE_PRIMARY
    priority = 82
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.marketplace.models import (
                BuyerRegistryEntry,
                MarketDemandListing,
            )
            buyer = BuyerRegistryEntry.objects.filter(user=user).first()
            if buyer is not None:
                qs = (
                    MarketDemandListing.objects.filter(
                        buyer=buyer,
                        status=MarketDemandListing.Status.OPEN,
                    )
                    .order_by("-created_at")[:5]
                )
                for d in qs:
                    sublabel = d.get_kind_display()
                    if d.deadline:
                        sublabel += f" · closes {timezone.localtime(d.deadline).strftime('%b %d')}"
                    items.append({
                        "label": d.title,
                        "sublabel": sublabel,
                        "badge": "Open",
                        "badge_color": "emerald",
                        "href": reverse("smehub_marketplace:demand_responses", args=[d.pk]),
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "My open demands",
            "subtitle": "Live in the marketplace",
            "items": items,
            "empty_text": "No open demand listings.",
            "footer_href": reverse("smehub_marketplace:my_demand_listings"),
            "footer_label": "Manage demands",
        }


@register
class BuyerRecentSalesRecordsWidget(DashboardWidget):
    key = "sme.buyer_recent_sales"
    zone = ZONE_SIDE
    priority = 84
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.marketplace.models import (
                BuyerRegistryEntry,
                SalesRecord,
            )
            buyer = BuyerRegistryEntry.objects.filter(user=user).first()
            if buyer is not None:
                qs = (
                    SalesRecord.objects.filter(buyer_entry=buyer)
                    .select_related("product_listing", "business")
                    .order_by("-sale_date")[:5]
                )
                color_map = {
                    "pending_details": "neutral",
                    "recorded": "sky",
                    "verified": "emerald",
                    "disputed": "rose",
                    "cancelled": "neutral",
                }
                for s in qs:
                    amount = f"{s.gross_amount:,.0f} {s.currency}" if s.gross_amount else ""
                    label = s.product_listing.name if s.product_listing else (s.business.name if s.business else "Sale")
                    items.append({
                        "label": label,
                        "sublabel": s.sale_date.strftime("%b %d") if s.sale_date else "",
                        "badge": s.get_status_display(),
                        "badge_color": color_map.get(s.status, "neutral"),
                        "value": amount,
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Recent sales",
            "subtitle": "Verified marketplace activity",
            "items": items,
            "empty_text": "No sales recorded yet.",
        }


# ── Service provider: upcoming advisory sessions + KPIs ─────────────────


@register
class ServiceProviderUpcomingSessionsKPI(DashboardWidget):
    key = "sme.kpi.service_upcoming_sessions"
    zone = ZONE_KPI
    priority = 81
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from apps.smehub.linkage.models import (
                AdvisorySession,
                ServiceProviderEntry,
            )
            spe = ServiceProviderEntry.objects.filter(managed_by=user).first()
            if spe is not None:
                n = AdvisorySession.objects.filter(
                    service_provider=spe,
                    status=AdvisorySession.Status.SCHEDULED,
                    starts_at__gte=timezone.now(),
                ).count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Upcoming sessions",
            "value": f"{n:,}",
            "accent": "sky" if n else "primary",
            "hint": "Scheduled advisory sessions",
            "href": reverse("smehub_linkage:advisory_sessions"),
        }


@register
class ServiceProviderActiveConnectionsKPI(DashboardWidget):
    key = "sme.kpi.service_active_connections"
    zone = ZONE_KPI
    priority = 82
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                Connection,
                ServiceProviderEntry,
            )
            spe = ServiceProviderEntry.objects.filter(managed_by=user).first()
            if spe is not None:
                ct = ContentType.objects.get_for_model(ServiceProviderEntry)
                n = Connection.objects.filter(
                    target_content_type=ct,
                    target_object_id=str(spe.pk),
                    status=Connection.Status.ACTIVE,
                ).count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Active connections",
            "value": f"{n:,}",
            "hint": "Linked entrepreneurs",
        }


@register
class ServiceProviderPendingRequestsWidget(DashboardWidget):
    key = "sme.service_pending_requests"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                ConnectionRequest,
                ServiceProviderEntry,
            )
            spe = ServiceProviderEntry.objects.filter(managed_by=user).first()
            if spe is not None:
                ct = ContentType.objects.get_for_model(ServiceProviderEntry)
                qs = (
                    ConnectionRequest.objects.filter(
                        target_content_type=ct,
                        target_object_id=str(spe.pk),
                        status=ConnectionRequest.Status.PENDING,
                    )
                    .select_related("entrepreneur__user", "business")
                    .order_by("-created_at")[:6]
                )
                for r in qs:
                    ent_name = (
                        r.entrepreneur.user.get_full_name() or r.entrepreneur.user.email
                    ) if r.entrepreneur and r.entrepreneur.user else "Entrepreneur"
                    items.append({
                        "label": ent_name,
                        "sublabel": r.business.name if r.business else "",
                        "badge": "Decide",
                        "badge_color": "amber",
                        "href": reverse("smehub_linkage:connection_request_decision", args=[r.pk]),
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Pending connection requests",
            "subtitle": "Awaiting your decision",
            "items": items,
            "empty_text": "No requests pending right now.",
        }


@register
class ServiceProviderUpcomingAdvisoryWidget(DashboardWidget):
    key = "sme.service_upcoming_advisory"
    zone = ZONE_SIDE
    priority = 82
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.smehub.linkage.models import (
                AdvisorySession,
                ServiceProviderEntry,
            )
            spe = ServiceProviderEntry.objects.filter(managed_by=user).first()
            if spe is not None:
                qs = (
                    AdvisorySession.objects.filter(
                        service_provider=spe,
                        status=AdvisorySession.Status.SCHEDULED,
                        starts_at__gte=timezone.now(),
                    )
                    .select_related("entrepreneur__user")
                    .order_by("starts_at")[:5]
                )
                for s in qs:
                    ent_name = (
                        s.entrepreneur.user.get_full_name() or s.entrepreneur.user.email
                    ) if s.entrepreneur and s.entrepreneur.user else "Entrepreneur"
                    items.append({
                        "label": ent_name,
                        "sublabel": timezone.localtime(s.starts_at).strftime("%a %b %d, %H:%M"),
                        "badge": s.get_type_display(),
                        "badge_color": "sky",
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Upcoming advisory sessions",
            "items": items,
            "empty_text": "No upcoming sessions booked.",
            "footer_href": reverse("smehub_linkage:advisory_sessions"),
            "footer_label": "Open sessions",
        }


# ── Partner: connections list + KPI ─────────────────────────────────────


@register
class PartnerActiveConnectionsKPI(DashboardWidget):
    key = "sme.kpi.partner_connections"
    zone = ZONE_KPI
    priority = 81
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                Connection,
                PartnerDirectoryEntry,
            )
            partner = PartnerDirectoryEntry.objects.filter(managed_by=user).first()
            if partner is not None:
                ct = ContentType.objects.get_for_model(PartnerDirectoryEntry)
                n = Connection.objects.filter(
                    target_content_type=ct,
                    target_object_id=str(partner.pk),
                    status=Connection.Status.ACTIVE,
                ).count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Active connections",
            "value": f"{n:,}",
            "hint": "Linked entrepreneurs",
        }


@register
class PartnerPendingRequestsKPI(DashboardWidget):
    key = "sme.kpi.partner_pending_requests"
    zone = ZONE_KPI
    priority = 82
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                ConnectionRequest,
                PartnerDirectoryEntry,
            )
            partner = PartnerDirectoryEntry.objects.filter(managed_by=user).first()
            if partner is not None:
                ct = ContentType.objects.get_for_model(PartnerDirectoryEntry)
                n = ConnectionRequest.objects.filter(
                    target_content_type=ct,
                    target_object_id=str(partner.pk),
                    status=ConnectionRequest.Status.PENDING,
                ).count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Pending requests",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "Awaiting your decision",
            "href": reverse("smehub_linkage:partner_connection_inbox"),
        }


@register
class PartnerPendingRequestsWidget(DashboardWidget):
    key = "sme.partner_pending_requests"
    zone = ZONE_PRIMARY
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                ConnectionRequest,
                PartnerDirectoryEntry,
            )
            partner = PartnerDirectoryEntry.objects.filter(managed_by=user).first()
            if partner is not None:
                ct = ContentType.objects.get_for_model(PartnerDirectoryEntry)
                qs = (
                    ConnectionRequest.objects.filter(
                        target_content_type=ct,
                        target_object_id=str(partner.pk),
                        status=ConnectionRequest.Status.PENDING,
                    )
                    .select_related("entrepreneur__user", "business")
                    .order_by("-created_at")[:6]
                )
                for r in qs:
                    ent_name = (
                        r.entrepreneur.user.get_full_name() or r.entrepreneur.user.email
                    ) if r.entrepreneur and r.entrepreneur.user else "Entrepreneur"
                    items.append({
                        "label": ent_name,
                        "sublabel": r.business.name if r.business else "",
                        "badge": "Decide",
                        "badge_color": "amber",
                        "href": reverse("smehub_linkage:connection_request_decision", args=[r.pk]),
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Pending connection requests",
            "subtitle": "Awaiting your decision",
            "items": items,
            "empty_text": "No requests pending right now.",
            "footer_href": reverse("smehub_linkage:partner_connection_inbox"),
            "footer_label": "Open inbox",
        }


@register
class PartnerActiveConnectionsListWidget(DashboardWidget):
    key = "sme.partner_active_connections"
    zone = ZONE_SIDE
    priority = 82
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from django.contrib.contenttypes.models import ContentType

            from apps.smehub.linkage.models import (
                Connection,
                PartnerDirectoryEntry,
            )
            partner = PartnerDirectoryEntry.objects.filter(managed_by=user).first()
            if partner is not None:
                ct = ContentType.objects.get_for_model(PartnerDirectoryEntry)
                qs = (
                    Connection.objects.filter(
                        target_content_type=ct,
                        target_object_id=str(partner.pk),
                        status=Connection.Status.ACTIVE,
                    )
                    .select_related("entrepreneur__user")
                    .order_by("-updated_at")[:5]
                )
                for c in qs:
                    ent_name = (
                        c.entrepreneur.user.get_full_name() or c.entrepreneur.user.email
                    ) if c.entrepreneur and c.entrepreneur.user else "Entrepreneur"
                    items.append({
                        "label": ent_name,
                        "sublabel": timezone.localtime(c.updated_at).strftime("Linked %b %d") if c.updated_at else "",
                        "badge": "Active",
                        "badge_color": "emerald",
                    })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Active connections",
            "items": items,
            "empty_text": "No active partner connections yet.",
            "footer_href": reverse("smehub_linkage:partner_mou_list"),
            "footer_label": "View MoUs",
        }


# ── SME-Hub admin: extra KPIs to fill the row ───────────────────────────


def _sme_admin_only(user) -> bool:
    """Show extra SME KPIs only to dedicated SME admins / programme officers.
    Platform admins already see the same numbers via the cross-module
    Platform Overview, so we don't stack them on the KPI row.
    """
    from apps.core.dashboard.role_groups import is_admin
    return has_role(user, SME_ADMIN_ROLES) and not is_admin(user)


@register
class SmeAdminVerifiedBusinessesKPI(DashboardWidget):
    key = "sme.kpi.admin_verified_businesses"
    zone = ZONE_KPI
    priority = 101
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from apps.smehub.onboarding.models import Business
            n = Business.objects.filter(verification_status="verified").count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Verified businesses",
            "value": f"{n:,}",
            "hint": "Across SME-Hub",
        }


@register
class SmeAdminFundingApplicationsKPI(DashboardWidget):
    key = "sme.kpi.admin_funding_apps"
    zone = ZONE_KPI
    priority = 102
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from apps.smehub.investment.models import FundingApplication
            n = FundingApplication.objects.exclude(status="withdrawn").count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Funding applications",
            "value": f"{n:,}",
            "hint": "Lifetime submissions",
            "href": reverse("smehub_investment:admin_dashboard"),
        }


@register
class SmeAdminShowcaseApplicationsKPI(DashboardWidget):
    key = "sme.kpi.admin_showcase_apps"
    zone = ZONE_KPI
    priority = 103
    template = "core/dashboard/widgets/kpi_card.html"

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

    def get_context(self, user) -> dict:
        n = 0
        try:
            from apps.smehub.showcasing.models import ShowcaseApplication
            n = ShowcaseApplication.objects.filter(
                status__in=[
                    ShowcaseApplication.Status.SUBMITTED,
                    ShowcaseApplication.Status.UNDER_REVIEW,
                ]
            ).count()
        except Exception:  # noqa: BLE001
            pass
        return {
            "label": "Showcase applications",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "Submitted / under review",
            "href": reverse("smehub_showcasing:admin_dashboard"),
        }
