"""MEL (Monitoring, Evaluation & Learning) dashboard widgets."""
from __future__ import annotations

import json

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 MEL_ROLES, has_role


def _mel_models():
    from apps.mel.indicators.models import DataPoint, Indicator
    from apps.mel.reports.models import Report, ReportStatus
    from apps.mel.tracking.models import Activity, ActivityStatus

    return Indicator, DataPoint, Report, ReportStatus, Activity, ActivityStatus


# ── MEL KPIs ─────────────────────────────────────────────────────────────


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

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


@register
class MELIndicatorsKPI(_MELKPIBase):
    key = "mel.kpi.indicators"
    priority = 70

    def get_context(self, user) -> dict:
        Indicator, *_ = _mel_models()
        n = Indicator.objects.filter(is_active=True).count()
        return {
            "label": "Active indicators",
            "value": f"{n:,}",
            "hint": "Tracked across logframes",
            "href": reverse("mel_indicators:indicator_list"),
        }


@register
class MELPendingDataPointsKPI(_MELKPIBase):
    key = "mel.kpi.pending_data"
    priority = 71

    def get_context(self, user) -> dict:
        _, DataPoint, *_ = _mel_models()
        n = DataPoint.objects.filter(status=DataPoint.Status.PENDING).count()
        return {
            "label": "Pending verification",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "Data points awaiting review" if n else "All verified",
            "href": reverse("mel_indicators:data_entry"),
        }


@register
class MELActivitiesAtRiskKPI(_MELKPIBase):
    key = "mel.kpi.activities_at_risk"
    priority = 72

    def get_context(self, user) -> dict:
        _, _, _, _, Activity, _ = _mel_models()
        today = timezone.now().date()
        # Activities scheduled to end before today but still not completed
        n = (
            Activity.objects.filter(scheduled_end__lt=today)
            .exclude(status="completed")
            .count()
        )
        return {
            "label": "Activities at risk",
            "value": f"{n:,}",
            "accent": "rose" if n else "primary",
            "hint": "Past scheduled end & not done" if n else "On schedule",
        }


@register
class MELDeviationFlagsKPI(_MELKPIBase):
    key = "mel.kpi.deviation_flags"
    priority = 73

    def get_context(self, user) -> dict:
        _, _, _, _, Activity, _ = _mel_models()
        n = Activity.objects.filter(deviation_flag=True).count()
        return {
            "label": "Deviation flags",
            "value": f"{n:,}",
            "accent": "amber" if n else "primary",
            "hint": "Activities flagged for review",
        }


# ── MEL Pending Data Points list ─────────────────────────────────────────


@register
class PendingDataPointsWidget(DashboardWidget):
    key = "mel.pending_data_points"
    zone = ZONE_PRIMARY
    priority = 70
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        _, DataPoint, *_ = _mel_models()
        qs = (
            DataPoint.objects.filter(status=DataPoint.Status.PENDING)
            .select_related("indicator")
            .order_by("-reported_at")[:6]
        )
        items = []
        for dp in qs:
            items.append({
                "label": f"{dp.indicator.code}: {dp.indicator.name[:60]}",
                "sublabel": f"{dp.period_label} • value {dp.value}",
                "badge": "Pending",
                "badge_color": "amber",
                "href": reverse("mel_indicators:data_entry"),
            })
        return {
            "title": "Data points to verify",
            "subtitle": "Awaiting verification",
            "items": items,
            "empty_text": "All data points are verified.",
            "footer_href": reverse("mel_indicators:data_entry"),
            "footer_label": "Open data entry",
        }


# ── MEL Activities at risk list ──────────────────────────────────────────


@register
class ActivitiesAtRiskWidget(DashboardWidget):
    key = "mel.activities_at_risk"
    zone = ZONE_PRIMARY
    priority = 75
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        _, _, _, _, Activity, _ = _mel_models()
        today = timezone.now().date()
        qs = (
            Activity.objects.filter(scheduled_end__lt=today)
            .exclude(status="completed")
            .select_related("logframe_row")
            .order_by("scheduled_end")[:5]
        )
        items = []
        for a in qs:
            overdue_days = (today - a.scheduled_end).days
            items.append({
                "label": a.name,
                "sublabel": f"Scheduled end {a.scheduled_end:%b %d, %Y}",
                "badge": f"{overdue_days}d overdue",
                "badge_color": "rose",
            })
        return {
            "title": "Activities at risk",
            "subtitle": "Past scheduled end and not completed",
            "items": items,
            "empty_text": "No activities overdue.",
        }


# ── MEL Recent Reports list ──────────────────────────────────────────────


@register
class RecentReportsWidget(DashboardWidget):
    key = "mel.recent_reports"
    zone = ZONE_SIDE
    priority = 70
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        _, _, Report, ReportStatus, _, _ = _mel_models()
        qs = Report.objects.select_related("template").order_by("-generated_at")[:5]
        items = []
        for r in qs:
            color = "emerald" if r.status == "published" else ("sky" if r.status == "approved" else "neutral")
            items.append({
                "label": r.template.name,
                "sublabel": r.period_label,
                "badge": r.get_status_display(),
                "badge_color": color,
                "href": reverse("mel_reports:report_detail", args=[r.pk]),
            })
        return {
            "title": "Recent reports",
            "items": items,
            "empty_text": "No reports generated yet.",
            "footer_href": reverse("mel_reports:report_list"),
            "footer_label": "All reports",
        }


# ── MEL: Data points by status donut ─────────────────────────────────────


@register
class MELDataPointsDonut(DashboardWidget):
    key = "mel.data_points_donut"
    zone = ZONE_PRIMARY
    priority = 78
    template = "core/dashboard/widgets/donut_card.html"

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

    def get_context(self, user) -> dict:
        from django.db.models import Count

        _, DataPoint, *_ = _mel_models()
        rows = DataPoint.objects.values("status").annotate(c=Count("id"))
        palette = {
            DataPoint.Status.PENDING: ("Pending", "#d97706"),
            DataPoint.Status.VERIFIED: ("Verified", "#059669"),
            DataPoint.Status.REJECTED: ("Rejected", "#e11d48"),
        }
        segs, labels, values, colors = [], [], [], []
        total = 0
        for r in rows:
            if r["status"] not in palette:
                continue
            label, color = palette[r["status"]]
            segs.append({"label": label, "value": r["c"], "color": color})
            labels.append(label)
            values.append(r["c"])
            colors.append(color)
            total += r["c"]
        return {
            "title": "Data points by status",
            "segments": segs,
            "labels_json": json.dumps(labels),
            "values_json": json.dumps(values),
            "colors_json": json.dumps(colors),
            "total": total,
            "total_label": "total",
            "footer_href": reverse("mel_indicators:data_entry"),
            "footer_label": "Data entry",
        }


# ── MEL: Indicators by source-module (coverage) donut ────────────────────


@register
class MELIndicatorCoverageDonut(DashboardWidget):
    key = "mel.indicator_coverage_donut"
    zone = ZONE_PRIMARY
    priority = 79
    template = "core/dashboard/widgets/donut_card.html"

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

    def get_context(self, user) -> dict:
        from django.db.models import Count
        Indicator, *_ = _mel_models()
        rows = (
            Indicator.objects.filter(is_active=True)
            .values("source_module").annotate(c=Count("id"))
            .order_by("-c")
        )
        palette = [
            "#6b5317", "#0284c7", "#059669", "#d97706", "#7c3aed", "#94a3b8", "#e11d48", "#475569",
        ]
        segs, labels, values, colors = [], [], [], []
        total = 0
        for i, r in enumerate(rows):
            label = r["source_module"] or "Manual"
            color = palette[i % len(palette)]
            segs.append({"label": label, "value": r["c"], "color": color})
            labels.append(label)
            values.append(r["c"])
            colors.append(color)
            total += r["c"]
        return {
            "title": "Indicators by source",
            "subtitle": "Coverage across modules",
            "segments": segs,
            "labels_json": json.dumps(labels),
            "values_json": json.dumps(values),
            "colors_json": json.dumps(colors),
            "total": total,
            "total_label": "indicators",
            "footer_href": reverse("mel_indicators:indicator_list"),
            "footer_label": "All indicators",
        }


# ── MEL: Recent indicator updates ────────────────────────────────────────


@register
class MELRecentDataPointsWidget(DashboardWidget):
    key = "mel.recent_data_points"
    zone = ZONE_SIDE
    priority = 75
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        _, DataPoint, *_ = _mel_models()
        qs = (
            DataPoint.objects.select_related("indicator")
            .order_by("-reported_at")[:6]
        )
        items = []
        color_map = {
            DataPoint.Status.PENDING: "amber",
            DataPoint.Status.VERIFIED: "emerald",
            DataPoint.Status.REJECTED: "rose",
        }
        for dp in qs:
            items.append({
                "label": f"{dp.indicator.code}: {dp.indicator.name[:50]}",
                "sublabel": f"{dp.period_label} · {dp.value}",
                "badge": dp.get_status_display(),
                "badge_color": color_map.get(dp.status, "neutral"),
            })
        return {
            "title": "Latest data points",
            "items": items,
            "empty_text": "No data points recorded yet.",
        }


# ── MEL Quick Actions ────────────────────────────────────────────────────


@register
class MELQuickActions(DashboardWidget):
    key = "mel.actions"
    zone = ZONE_ACTIONS
    priority = 50
    template = "core/dashboard/widgets/quick_actions.html"

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

    def get_context(self, user) -> dict:
        return {
            "actions": [
                {
                    "label": "Enter data",
                    "href": reverse("mel_indicators:data_entry"),
                    "primary": True,
                    "icon": "M12 4v16m8-8H4",
                },
                {
                    "label": "Indicators",
                    "href": reverse("mel_indicators:indicator_list"),
                    "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",
                },
                {
                    "label": "Logframes",
                    "href": reverse("mel_indicators:logframe_list"),
                    "icon": "M4 6h16M4 10h16M4 14h16M4 18h16",
                },
                {
                    "label": "Generate report",
                    "href": reverse("mel_reports:report_generate"),
                    "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",
                },
            ],
        }


# ── MEL: open survey dispatches awaiting responses ───────────────────────


@register
class MELOpenSurveyDispatchesWidget(DashboardWidget):
    key = "mel.open_survey_dispatches"
    zone = ZONE_SIDE
    priority = 80
    template = "core/dashboard/widgets/list_card.html"

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

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

            from apps.mel.feedback.models import Survey, SurveyDispatch
            qs = (
                Survey.objects.filter(status="active")
                .annotate(
                    open_count=Count(
                        "dispatches",
                        filter=Q(dispatches__status__in=[
                            SurveyDispatch.Status.SENT,
                            SurveyDispatch.Status.REMINDED,
                        ]),
                    ),
                )
                .filter(open_count__gt=0)
                .order_by("-activated_at")[:5]
            )
            for s in qs:
                items.append({
                    "label": s.title,
                    "sublabel": s.get_trigger_display() if hasattr(s, "get_trigger_display") else "",
                    "value": f"{s.open_count}",
                    "href": reverse("mel_feedback:survey_results", args=[s.slug]),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Open survey dispatches",
            "subtitle": "Awaiting responses",
            "items": items,
            "empty_text": "No active surveys with open dispatches.",
            "footer_href": reverse("mel_feedback:survey_list"),
            "footer_label": "All surveys",
        }


# ── MEL: unresolved compliance alerts ────────────────────────────────────


@register
class MELComplianceAlertsWidget(DashboardWidget):
    key = "mel.compliance_alerts"
    zone = ZONE_SIDE
    priority = 85
    template = "core/dashboard/widgets/list_card.html"

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

    def get_context(self, user) -> dict:
        items: list = []
        try:
            from apps.mel.reports.models import ComplianceAlert, ComplianceSeverity
            qs = (
                ComplianceAlert.objects.filter(resolved=False)
                .order_by("-created_at")[:5]
            )
            color_map = {
                ComplianceSeverity.LOW: "neutral",
                ComplianceSeverity.MEDIUM: "amber",
                ComplianceSeverity.HIGH: "rose",
                ComplianceSeverity.CRITICAL: "rose",
            }
            for a in qs:
                items.append({
                    "label": getattr(a, "code", None) or "Alert",
                    "sublabel": (a.message or "")[:80] if hasattr(a, "message") else "",
                    "badge": a.get_severity_display(),
                    "badge_color": color_map.get(a.severity, "amber"),
                })
        except Exception:  # noqa: BLE001
            pass
        return {
            "title": "Compliance alerts",
            "subtitle": "Unresolved",
            "items": items,
            "empty_text": "No unresolved compliance alerts.",
        }
