from __future__ import annotations

from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.views.generic import DetailView, TemplateView, View

from apps.core.permissions.mixins import RoleRequiredMixin
from apps.core.permissions.roles import UserRole
from apps.repository.analytics import services
from apps.repository.analytics.models import ResearchTrend
from apps.repository.documents.models import Author, Collection, Document, IntegrationOutbox
from apps.repository.documents.permissions import REPOSITORY_MANAGEMENT_ROLES


# Reuse the canonical management role set so Repository Admins, Collection
# Managers (KMO / K-Hub), and Editors-in-Chief can reach analytics — previously
# this tuple listed only three roles and locked everyone else out (FRREP-AR001/
# AR003/AR004/AR005).
REPO_MANAGER_ROLES = tuple(REPOSITORY_MANAGEMENT_ROLES)


class _ManagerMixin(RoleRequiredMixin):
    # SP9 (2026-07 interviews): the M&E Officer "accesses the analytics and
    # reporting section to pull usage data as M&E indicators" and exports
    # statistics for donor reporting — so they join the management set here,
    # for analytics only (no QA/collection rights elsewhere).
    allowed_roles = (*REPO_MANAGER_ROLES, UserRole.MEL_OFFICER)


def _parse_date(value: str | None):
    from datetime import datetime

    if not value:
        return None
    try:
        return datetime.strptime(value, "%Y-%m-%d").date()
    except ValueError:
        return None


class UsageDashboardView(_ManagerMixin, TemplateView):
    """FRREP-AR001 — dashboard now honours date-range / collection / document-type
    filters from the querystring instead of always calling
    ``get_usage_dashboard()`` with no arguments (the service already supported
    these kwargs; only the view/template weren't threading them through).
    No "institution" filter — Document has no institution concept of its own.
    """

    template_name = "analytics/usage_dashboard.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        params = self.request.GET

        date_from = _parse_date(params.get("date_from"))
        date_to = _parse_date(params.get("date_to"))
        collection_slug = (params.get("collection") or "").strip()
        document_type = (params.get("document_type") or "").strip()
        country = (params.get("country") or "").strip()
        institution = (params.get("institution") or "").strip()

        collection = None
        if collection_slug:
            collection = Collection.objects.filter(slug=collection_slug).first()

        ctx["stats"] = services.get_usage_dashboard(
            date_from=date_from,
            date_to=date_to,
            collection=collection,
            document_type=document_type or None,
            country=country or None,
            institution=institution or None,
        )
        ctx["corpus"] = services.get_corpus_analytics()
        ctx["outbox_pending"] = IntegrationOutbox.objects.filter(
            status=IntegrationOutbox.Status.PENDING
        ).count()
        ctx["zero_result_queries"] = services.get_zero_result_queries()
        ctx["search_conversion"] = services.get_search_conversion_rate()

        ctx["filter_collection_choices"] = Collection.objects.order_by("name")
        ctx["filter_document_type_choices"] = Document.DocumentType.choices
        ctx["filter_country_choices"] = list(
            Document.objects.filter(status=Document.Status.PUBLISHED)
            .exclude(country="")
            .values_list("country", flat=True)
            .distinct()
            .order_by("country")
        )
        ctx["filter_institution_choices"] = list(
            Document.objects.filter(status=Document.Status.PUBLISHED)
            .exclude(publisher="")
            .values_list("publisher", flat=True)
            .distinct()
            .order_by("publisher")
        )
        ctx["filter_date_from"] = params.get("date_from", "")
        ctx["filter_date_to"] = params.get("date_to", "")
        ctx["filter_collection"] = collection_slug
        ctx["filter_document_type"] = document_type
        ctx["filter_country"] = country
        ctx["filter_institution"] = institution
        ctx["filters_active"] = bool(
            date_from or date_to or collection_slug or document_type or country or institution
        )
        return ctx


class AIInsightsView(_ManagerMixin, TemplateView):
    template_name = "analytics/ai_insights.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["trends"] = ResearchTrend.objects.all()[:30]
        return ctx


class PerDocumentStatsView(_ManagerMixin, DetailView):
    model = Document
    slug_field = "document_uid"
    slug_url_kwarg = "uid"
    template_name = "analytics/document_stats.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["stats"] = services.get_per_document_stats(self.object)
        return ctx


class AuthorImpactView(LoginRequiredMixin, DetailView):
    """FRREP-AR003 — an author's impact dashboard. Scoped to authenticated users
    (registered authors + managers) rather than the anonymous public."""

    model = Author
    template_name = "analytics/author_impact.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["impact"] = services.get_author_impact(self.object)
        return ctx


class OpenAccessComplianceView(_ManagerMixin, TemplateView):
    """FRREP-AR004 — compliance now supports all three PRD dimensions
    (collection / document type / institution) via a ``?by=`` switcher.
    """

    template_name = "analytics/compliance_report.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        by = self.request.GET.get("by") or "collection"
        if by not in dict(services.COMPLIANCE_DIMENSIONS):
            by = "collection"
        ctx["by"] = by
        ctx["dimension_choices"] = services.COMPLIANCE_DIMENSIONS
        ctx["rows"] = services.get_open_access_compliance(by=by)
        return ctx


class CollectionAnalyticsView(_ManagerMixin, DetailView):
    model = Collection
    template_name = "analytics/collection_analytics.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["analytics"] = services.get_collection_analytics(self.object)
        return ctx


class IntegrationStatusDashboardView(_ManagerMixin, TemplateView):
    """Outbound integration health dashboard.

    PRD REPO-SP8 FRREP-INT009 — surface last-sync time and approximate data
    volume per target alongside the per-event status table.
    """

    template_name = "analytics/integration_status.html"

    def get_context_data(self, **kwargs):
        from django.db.models import Count, Max

        ctx = super().get_context_data(**kwargs)
        ctx["targets"] = IntegrationOutbox.Target.choices
        ctx["rows"] = IntegrationOutbox.objects.order_by("-created_at")[:100]

        per_target = {}
        agg = (
            IntegrationOutbox.objects.values("target", "status")
            .annotate(count=Count("id"))
        )
        for row in agg:
            per_target.setdefault(
                row["target"],
                {"sent": 0, "pending": 0, "failed": 0, "total_bytes": 0, "last_sent_at": None},
            )
            per_target[row["target"]][row["status"]] = row["count"]
        last_sent = (
            IntegrationOutbox.objects.filter(status=IntegrationOutbox.Status.SENT)
            .values("target")
            .annotate(latest=Max("sent_at"))
        )
        for row in last_sent:
            per_target.setdefault(
                row["target"],
                {"sent": 0, "pending": 0, "failed": 0, "total_bytes": 0, "last_sent_at": None},
            )
            per_target[row["target"]]["last_sent_at"] = row["latest"]
        # Approximate bytes transferred = serialised payload length per sent row.
        for row in IntegrationOutbox.objects.filter(
            status=IntegrationOutbox.Status.SENT
        ).only("target", "payload"):
            try:
                size = len(str(row.payload).encode("utf-8"))
            except Exception:
                size = 0
            per_target.setdefault(
                row.target,
                {"sent": 0, "pending": 0, "failed": 0, "total_bytes": 0, "last_sent_at": None},
            )
            per_target[row.target]["total_bytes"] += size

        ctx["target_metrics"] = [
            {
                "target": code,
                "label": label,
                "sent": per_target.get(code, {}).get("sent", 0),
                "pending": per_target.get(code, {}).get("pending", 0),
                "failed": per_target.get(code, {}).get("failed", 0),
                "total_bytes": per_target.get(code, {}).get("total_bytes", 0),
                "last_sent_at": per_target.get(code, {}).get("last_sent_at"),
            }
            for code, label in IntegrationOutbox.Target.choices
        ]
        return ctx


class ExportReportView(_ManagerMixin, View):
    """FRREP-AR006 — forward kind-specific extras (``by`` for compliance,
    ``slug`` for collection) so the export matches what the manager was
    actually looking at, instead of always exporting the default dimension.
    """

    def get(self, request, kind):
        fmt = request.GET.get("format", "xlsx").lower()
        params: dict[str, str] = {}
        if kind == "compliance":
            params["by"] = request.GET.get("by", "collection")
        elif kind == "collection":
            params["slug"] = request.GET.get("slug", "")
        data = services.export_report(kind, fmt, **params)
        if fmt in {"xlsx", "excel"}:
            content_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            filename = f"repository_{kind}.xlsx"
        elif fmt == "pdf":
            content_type = "application/pdf"
            filename = f"repository_{kind}.pdf"
        else:
            content_type = "text/csv"
            filename = f"repository_{kind}.csv"
        response = HttpResponse(data, content_type=content_type)
        response["Content-Disposition"] = f'attachment; filename="{filename}"'
        return response
