"""Stub AI-assist helpers used by the four `.ai-callout` surfaces.

Each helper returns a small dict — ``{label, summary, footnote, available}`` —
that the template renders inside an ``.ai-callout`` block. Today the values
are computed from the model itself (deterministic, no LLM call). The function
signatures are stable so a follow-up can swap in a LangChain-backed
implementation without touching templates.

The ``data-ai-stub="true"`` attribute on the rendered callouts lets a future
PR grep for the exact wiring points.
"""
from __future__ import annotations

from statistics import mean
from typing import Any


def reviewer_consensus(application) -> dict[str, Any]:
    """Application detail — summarise reviewer recommendations + average score."""
    reviews = application.reviews.exclude(submitted_at__isnull=True)
    total = reviews.count()
    if total == 0:
        return {
            "label": "AI reviewer summary",
            "summary": "No reviewer scores submitted yet. The summary will appear here once at least one reviewer scores this application.",
            "footnote": "Stub — replaces with model-generated consensus once reviews land.",
            "available": False,
        }
    approve_count = reviews.filter(recommendation_code__icontains="approve").count()
    scores = [r.score for r in reviews if getattr(r, "score", None) is not None]
    avg_label = f"{mean(scores):.1f}" if scores else "—"
    summary = (
        f"{approve_count} of {total} reviewer(s) recommend approval; mean score {avg_label}."
    )
    return {
        "label": "AI reviewer summary",
        "summary": summary,
        "footnote": "Auto-generated from submitted reviews. Always read the underlying scoring rubric before deciding.",
        "available": True,
    }


def funding_rule_advisor(funding_record) -> dict[str, Any]:
    """Funding detail — surface a rule-of-thumb suggestion based on neighbours."""
    from apps.rims.grants.models import FundingRecord

    siblings = (
        FundingRecord.objects.filter(status=FundingRecord.Status.APPROVED)
        .exclude(pk=funding_record.pk)
    )
    sibling_count = siblings.count()
    if sibling_count == 0:
        summary = (
            "No comparable approved funding records yet — base eligibility + reporting on "
            "the SRS minimums until a peer set is available."
        )
    else:
        avg = sum(r.amount or 0 for r in siblings) / sibling_count
        summary = (
            f"Peer set ({sibling_count} approved funding records) averages "
            f"{int(avg):,} {funding_record.currency}. Consider aligning reporting cadence to "
            f"the dominant frequency in the peer set."
        )
    return {
        "label": "AI rule advisor",
        "summary": summary,
        "footnote": "Heuristic guidance from prior approvals. Not a substitute for the director's policy judgement.",
        "available": True,
    }


def project_risk_snapshot(project) -> dict[str, Any]:
    """Project detail — derive a simple risk band from milestone status."""
    milestones = project.milestones.all()
    total = milestones.count()
    if total == 0:
        summary = "No milestones defined yet — risk score unavailable until the work breakdown is captured."
        band = "unknown"
    else:
        overdue = milestones.filter(status="overdue").count()
        accepted = milestones.filter(status="accepted").count()
        progress = (accepted / total) * 100 if total else 0
        risk_share = (overdue / total) * 100 if total else 0
        if risk_share == 0 and progress >= 60:
            band = "low"
        elif risk_share < 20:
            band = "moderate"
        else:
            band = "elevated"
        summary = (
            f"Risk band: {band}. {progress:.0f}% of milestones accepted, {risk_share:.0f}% currently overdue."
        )
    return {
        "label": "AI risk dashboard",
        "summary": summary,
        "footnote": "Derived from milestone state. A real model would also incorporate burn rate + reviewer comments.",
        "available": True,
        "band": band,
    }


def award_risk_summary(award) -> dict[str, Any]:
    """Award detail — slip detection from milestone + tranche history."""
    from django.utils import timezone

    project = getattr(award, "project", None)
    milestones = project.milestones.all() if project else []
    total_ms = len(milestones) if hasattr(milestones, "__len__") else milestones.count()
    overdue = sum(1 for m in milestones if getattr(m, "status", None) == "overdue")
    tranches = award.tranches.all()
    total_t = tranches.count() if hasattr(tranches, "count") else len(tranches)
    disbursed = sum(1 for t in tranches if getattr(t, "status", None) == "disbursed")
    today = timezone.now().date()
    days_to_end = (award.project_end_date - today).days if award.project_end_date else None

    parts: list[str] = []
    band = "low"
    if total_ms == 0:
        parts.append("No milestones captured yet — risk band unavailable until the work breakdown is recorded.")
        band = "unknown"
    else:
        slip_pct = (overdue / total_ms) * 100
        if slip_pct >= 25:
            band = "elevated"
        elif slip_pct >= 10:
            band = "moderate"
        parts.append(f"{overdue}/{total_ms} milestone(s) overdue ({slip_pct:.0f}%)")
    if total_t:
        parts.append(f"{disbursed}/{total_t} tranches disbursed")
    if days_to_end is not None:
        if days_to_end < 0:
            parts.append(f"Project end date passed {abs(days_to_end)} day(s) ago")
            band = "elevated"
        elif days_to_end < 30:
            parts.append(f"Project ends in {days_to_end} day(s)")
            if band == "low":
                band = "moderate"
    summary = f"Risk band: {band}. " + " · ".join(parts) + "."
    return {
        "label": "AI award risk",
        "summary": summary,
        "footnote": "Derived from milestone slippage, tranche disbursement, and end-date proximity. Real model would also weight finance variance.",
        "available": total_ms > 0 or total_t > 0,
        "band": band,
    }


def audit_anomaly_detector(rows) -> dict[str, Any]:
    """Operations audit log — flag spikes in delete or status-change activity."""
    rows_list = list(rows[:200]) if rows is not None else []
    if not rows_list:
        return {
            "label": "AI audit watchdog",
            "summary": "No audit activity in the current filter window — nothing to flag.",
            "footnote": "Stub heuristic. Real anomaly detection would compare against a rolling baseline.",
            "available": False,
        }
    by_action: dict[str, int] = {}
    for r in rows_list:
        action = (getattr(r, "action", "") or "").lower()
        by_action[action] = by_action.get(action, 0) + 1
    deletes = sum(v for k, v in by_action.items() if "delete" in k)
    status_changes = sum(v for k, v in by_action.items() if "status" in k or "transition" in k)
    parts = [f"{len(rows_list)} event(s) in view"]
    band = "low"
    if deletes >= 5:
        parts.append(f"{deletes} delete(s) — review chain of custody")
        band = "elevated"
    if status_changes >= 10:
        parts.append(f"{status_changes} status transition(s)")
        if band == "low":
            band = "moderate"
    summary = " · ".join(parts) + "."
    return {
        "label": "AI audit watchdog",
        "summary": summary,
        "footnote": "Heuristic spike-detector across the filtered slice. Always corroborate against source records.",
        "available": True,
        "band": band,
    }


def burn_rate_forecast(budgets) -> dict[str, Any]:
    """Finance budget overview — quick burn-rate hint across portfolio."""
    budgets_list = list(budgets[:200]) if budgets is not None else []
    if not budgets_list:
        return {
            "label": "AI burn-rate forecast",
            "summary": "Add award budgets to see consumption projections.",
            "footnote": "Stub heuristic.",
            "available": False,
        }
    total_amount = sum(float(getattr(b, "total_amount", None) or 0) for b in budgets_list)
    active = sum(1 for b in budgets_list if getattr(b, "is_active", False))
    summary = (
        f"{active} active budget(s) across {len(budgets_list)} total · "
        f"{int(total_amount):,} USD ceiling under management."
    )
    return {
        "label": "AI burn-rate forecast",
        "summary": summary,
        "footnote": "A real forecast would project remaining months based on the trailing burn curve and milestone-gated tranches.",
        "available": True,
    }


def reconciliation_hint(disbursement) -> dict[str, Any]:
    """Finance disbursement detail — guidance about the next reconciliation step."""
    exec_status = getattr(getattr(disbursement, "execution", None), "status", None)
    recon = getattr(disbursement, "reconciliation", None)
    recon_status = getattr(recon, "status", None) if recon else None
    if recon_status == "reconciled":
        summary = "Reconciliation closed. The bank/mobile-money statement excerpt is on file — no further action needed."
        available = True
    elif disbursement.status == "paid" and exec_status == "settled":
        summary = "Payment settled but not yet reconciled. Attach the statement excerpt and confirm clearance to close the loop (FRFM047)."
        available = True
    elif disbursement.status == "approved":
        summary = "Approval recorded. Execute the payment via the chosen channel, capture the receipt, then proceed to reconciliation."
        available = True
    else:
        summary = "Request is moving through the standard approval queue — reconciliation guidance unlocks once the payment is executed."
        available = False
    return {
        "label": "AI reconciliation hint",
        "summary": summary,
        "footnote": "Stub heuristic. Real model would match the execution reference against statement excerpts automatically.",
        "available": available,
    }


def scholar_academic_trend(scholar) -> dict[str, Any]:
    """Scholar detail — peek at progress reports + stipend cadence."""
    stipends = scholar.stipends.all()
    paid_count = stipends.filter(status="paid").count()
    pending_count = stipends.filter(status="pending").count()
    cancelled_count = stipends.filter(status="cancelled").count()
    if not stipends.exists():
        summary = "No stipend history yet — the academic-trend summary unlocks after the first payment cycle."
        available = False
    else:
        summary = (
            f"{paid_count} stipend(s) paid, {pending_count} pending, {cancelled_count} cancelled. "
        )
        if cancelled_count >= 2:
            summary += "Cancellation spike — flag for the programme officer."
        elif scholar.status in ("paused", "deferred"):
            summary += "Scholar studies are paused — stipend cadence paused."
        elif scholar.status == "active":
            summary += "Cadence is regular; no anomaly detected."
        else:
            summary += f"Status: {scholar.get_status_display()}."
        available = True
    return {
        "label": "AI academic trend",
        "summary": summary,
        "footnote": "Stub heuristic. A real model would ingest progress reports + tracer data.",
        "available": available,
    }
