"""Compare BudgetCommitment (procurement's own ledger) against the GL
encumbrance balance (accounting.JournalEntry lines on the Encumbrance
account), per budget item. The two are written independently — this finds
where they've drifted apart."""
from decimal import Decimal

from django.db.models import Sum

from apps.accounting.models import JournalLine
from apps.finance.models import BudgetItem
from .models import BudgetCommitment


def reconcile_budget_commitments(organization):
    """Return a list of {budget_item_id, budget_item_name, active_commitments,
    gl_encumbrance_balance, drift} for every budget item with a nonzero
    difference between active BudgetCommitment totals and the outstanding GL
    encumbrance balance."""
    committed_by_item = dict(
        BudgetCommitment.objects.filter(status="active", budget_item__category__organization=organization)
        .values("budget_item_id")
        .annotate(total=Sum("amount"))
        .values_list("budget_item_id", "total")
    )
    gl_by_item = dict(
        JournalLine.objects.filter(
            journal__budget_item__isnull=False, account__code="1900",
            journal__organization=organization, account__organization=organization,
        )
        .values("journal__budget_item_id")
        .annotate(balance=Sum("debit_idr") - Sum("credit_idr"))
        .values_list("journal__budget_item_id", "balance")
    )
    budget_item_ids = set(committed_by_item) | set(gl_by_item)
    names = dict(BudgetItem.objects.filter(pk__in=budget_item_ids, category__organization=organization).values_list("pk", "name"))

    findings = []
    for budget_item_id in budget_item_ids:
        committed = committed_by_item.get(budget_item_id) or Decimal("0")
        gl_balance = gl_by_item.get(budget_item_id) or Decimal("0")
        drift = committed - gl_balance
        if drift != 0:
            findings.append({
                "budget_item_id": str(budget_item_id).replace("-", ""),
                "budget_item_name": names.get(budget_item_id, "(deleted)"),
                "active_commitments": committed,
                "gl_encumbrance_balance": gl_balance,
                "drift": drift,
            })
    return findings
