"""Petty cash module API views."""
import json
from datetime import datetime
from decimal import Decimal

from django.db import transaction
from django.db.models import Sum
from django.utils import timezone
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from human_resource.models import StaffProfile
from system_administration.models import CompanyProfile
from system_administration.utils import check_if_system_administrator

from .journal_posting import PostingError
from .models import (
    ChartOfAccount,
    FinanceAuditLog,
    PettyCashDisbursement,
    PettyCashFund,
    PettyCashReconciliation,
    PettyCashReplenishment,
    PettyCashRequest,
    PettyCashRetirement,
    PettyCashRetirementLine,
)
from .petty_cash_posting import post_disbursement, post_fund_opening, post_replenishment, post_retirement, sync_petty_cash_running_balance


def _auth(request):
    serial = request.data.get("serial_number")
    if not serial:
        raise ValueError("serial_number required")
    company_profile = CompanyProfile.objects.get(company_serial_number=serial)
    staff_profile = StaffProfile.objects.filter(user=request.user).select_related(
        "company_department", "company_branch", "staff_position",
    ).first()
    if not staff_profile:
        raise ValueError("staff_profile required")
    return company_profile, staff_profile


def _require_finance(staff_profile):
    if check_if_system_administrator(staff_profile):
        return True
    dept = getattr(staff_profile, "company_department", None)
    return bool(dept and dept.department_name == "finance_and_accounting")


def _staff_name(staff) -> str:
    if not staff:
        return ""
    return " ".join(filter(None, [getattr(staff, "first_name", ""), getattr(staff, "last_name", "")])).strip()


def _account_label(account) -> str:
    if not account:
        return ""
    return f"{account.account_number} — {account.account_name}"


def _fund_map(fund: PettyCashFund) -> dict:
    return {
        "fund_id": str(fund.id),
        "fund_number": fund.fund_number,
        "fund_name": fund.fund_name,
        "branch_id": str(fund.company_branch_id) if fund.company_branch_id else "",
        "branch_name": fund.company_branch.branch_name if fund.company_branch else "",
        "custodian_id": str(fund.custodian_id) if fund.custodian_id else "",
        "custodian_name": _staff_name(fund.custodian),
        "petty_cash_account_id": str(fund.petty_cash_account_id) if fund.petty_cash_account_id else "",
        "petty_cash_account_label": _account_label(fund.petty_cash_account),
        "employee_advances_account_id": str(fund.employee_advances_account_id) if fund.employee_advances_account_id else "",
        "employee_advances_account_label": _account_label(fund.employee_advances_account),
        "bank_account_id": str(fund.bank_account_id) if fund.bank_account_id else "",
        "bank_account_label": _account_label(fund.bank_account),
        "imprest_amount": str(fund.imprest_amount),
        "current_balance": str(fund.current_balance),
        "is_active": "true" if fund.is_active else "false",
        "notes": fund.notes or "",
        "created_on": fund.created_on.strftime("%Y-%m-%d %H:%M") if fund.created_on else "",
    }


def _request_map(req: PettyCashRequest) -> dict:
    disbursement = getattr(req, "disbursement", None)
    retirement = getattr(disbursement, "retirement", None) if disbursement else None
    return {
        "request_id": str(req.id),
        "request_number": req.request_number,
        "fund_id": str(req.fund_id),
        "fund_name": req.fund.fund_name if req.fund else "",
        "requested_by_id": str(req.requested_by_id) if req.requested_by_id else "",
        "requested_by_name": _staff_name(req.requested_by),
        "purpose": req.purpose,
        "amount": str(req.amount),
        "status": req.status,
        "approved_by_name": _staff_name(req.approved_by),
        "approved_on": req.approved_on.strftime("%Y-%m-%d %H:%M") if req.approved_on else "",
        "rejection_reason": req.rejection_reason or "",
        "disbursement_number": disbursement.disbursement_number if disbursement else "",
        "retirement_status": retirement.status if retirement else "",
        "created_on": req.created_on.strftime("%Y-%m-%d %H:%M") if req.created_on else "",
    }


def _audit(branch, staff, entity_type, entity_id, action, notes="", before=None, after=None):
    FinanceAuditLog.objects.create(
        company_branch=branch,
        entity_type=entity_type,
        entity_id=str(entity_id),
        action=action,
        actor=staff,
        notes=notes,
        before_data=json.dumps(before or {}),
        after_data=json.dumps(after or {}),
    )


def _fund_queryset(company_profile, staff_profile, branch_id=None):
    qs = PettyCashFund.objects.filter(
        recycle_bin=False,
        company_branch__company_profile=company_profile,
    ).select_related(
        "company_branch", "custodian", "petty_cash_account",
        "employee_advances_account", "bank_account",
    )
    if branch_id:
        qs = qs.filter(company_branch_id=branch_id)
    elif not check_if_system_administrator(staff_profile) and staff_profile.company_branch_id:
        qs = qs.filter(company_branch_id=staff_profile.company_branch_id)
    return qs


def _fund_has_activity(fund: PettyCashFund) -> bool:
    if PettyCashDisbursement.objects.filter(request__fund=fund).exists():
        return True
    if PettyCashReplenishment.objects.filter(fund=fund).exists():
        return True
    if PettyCashReconciliation.objects.filter(fund=fund).exists():
        return True
    if PettyCashRequest.objects.filter(fund=fund, recycle_bin=False).exclude(
        status__in=["draft", "rejected"],
    ).exists():
        return True
    return False


def _fund_can_delete(fund: PettyCashFund):
    if PettyCashDisbursement.objects.filter(request__fund=fund).exists():
        return False, "Fund has disbursements and cannot be deleted."
    if PettyCashReplenishment.objects.filter(fund=fund).exists():
        return False, "Fund has replenishments and cannot be deleted."
    active = PettyCashRequest.objects.filter(
        fund=fund, recycle_bin=False, status__in=["pending_approval", "approved"],
    ).exists()
    if active:
        return False, "Fund has pending or approved requests."
    return True, ""


def _optional_fk_id(data, key):
    if key not in data:
        return None
    val = data.get(key)
    return int(val) if val else None


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_dashboard(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        branch_id = request.data.get("branch_id")
        funds = _fund_queryset(company_profile, staff_profile, branch_id)
        fund_ids = list(funds.values_list("id", flat=True))

        pending_requests = PettyCashRequest.objects.filter(
            fund_id__in=fund_ids, recycle_bin=False, status="pending_approval",
        ).count()
        approved_requests = PettyCashRequest.objects.filter(
            fund_id__in=fund_ids, recycle_bin=False, status="approved",
        ).count()
        open_advances = PettyCashDisbursement.objects.filter(
            request__fund_id__in=fund_ids,
            retirement__isnull=True,
        ).aggregate(total=Sum("amount"))["total"] or Decimal("0")
        pending_reconciliations = PettyCashReconciliation.objects.filter(
            fund_id__in=fund_ids, status="pending",
        ).count()

        payload = {
            "fund_count": funds.count(),
            "total_imprest": str(funds.aggregate(total=Sum("imprest_amount"))["total"] or Decimal("0")),
            "total_balance": str(funds.aggregate(total=Sum("current_balance"))["total"] or Decimal("0")),
            "pending_requests": pending_requests,
            "approved_awaiting_disbursement": approved_requests,
            "outstanding_advances": str(open_advances),
            "pending_reconciliations": pending_reconciliations,
            "fund_list": [_fund_map(f) for f in funds[:20]],
        }
        return Response({"message": "true", "payload": payload})
    except CompanyProfile.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Company not found."}}, status=404)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_funds(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        branch_id = request.data.get("branch_id")
        funds = _fund_queryset(company_profile, staff_profile, branch_id)

        if request.data.get("action") == "create":
            branch_id = request.data.get("branch_id") or staff_profile.company_branch_id
            fund = PettyCashFund.objects.create(
                company_branch_id=branch_id,
                fund_name=request.data.get("fund_name", "Petty Cash Fund"),
                petty_cash_account_id=_optional_fk_id(request.data, "petty_cash_account_id"),
                employee_advances_account_id=_optional_fk_id(request.data, "employee_advances_account_id"),
                bank_account_id=_optional_fk_id(request.data, "bank_account_id"),
                custodian_id=_optional_fk_id(request.data, "custodian_id"),
                imprest_amount=Decimal(str(request.data.get("imprest_amount") or 0)),
                current_balance=Decimal(str(request.data.get("imprest_amount") or 0)),
                notes=request.data.get("notes", ""),
                created_by=staff_profile,
            )
            _audit(fund.company_branch, staff_profile, "petty_cash_fund", fund.id, "create", after=_fund_map(fund))
            if fund.imprest_amount > 0 and fund.petty_cash_account_id:
                try:
                    post_fund_opening(fund=fund, staff=staff_profile)
                except PostingError:
                    sync_petty_cash_running_balance(fund)
            return Response({"message": "true", "payload": {"fund": _fund_map(fund)}})

        if request.data.get("action") == "update":
            fund = funds.get(id=request.data.get("fund_id"))
            before = _fund_map(fund)
            if request.data.get("fund_name"):
                fund.fund_name = request.data.get("fund_name")
            if "notes" in request.data:
                fund.notes = request.data.get("notes", "")
            if "custodian_id" in request.data:
                fund.custodian_id = _optional_fk_id(request.data, "custodian_id")
            for field in ("petty_cash_account_id", "employee_advances_account_id", "bank_account_id"):
                if field in request.data:
                    setattr(fund, field, _optional_fk_id(request.data, field))
            if "is_active" in request.data:
                fund.is_active = str(request.data.get("is_active")).lower() == "true"
            if "imprest_amount" in request.data:
                new_imprest = Decimal(str(request.data.get("imprest_amount") or 0))
                if _fund_has_activity(fund) and new_imprest != fund.imprest_amount:
                    return Response({
                        "message": "false",
                        "payload": {"error": "Cannot change imprest after fund activity."},
                    }, status=400)
                if not _fund_has_activity(fund):
                    fund.imprest_amount = new_imprest
                    fund.current_balance = new_imprest
            fund.save()
            fund.refresh_from_db()
            sync_petty_cash_running_balance(fund)
            _audit(
                fund.company_branch, staff_profile, "petty_cash_fund", fund.id, "update",
                before=before, after=_fund_map(fund),
            )
            return Response({"message": "true", "payload": {"fund": _fund_map(fund)}})

        if request.data.get("action") == "sync_gl":
            fund = funds.get(id=request.data.get("fund_id"))
            sync_petty_cash_running_balance(fund)
            return Response({
                "message": "true",
                "payload": {
                    "fund": _fund_map(fund),
                    "gl_running_balance": fund.petty_cash_account.running_balance if fund.petty_cash_account else "0.00",
                },
            })

        if request.data.get("action") == "delete":
            fund = funds.get(id=request.data.get("fund_id"))
            ok, msg = _fund_can_delete(fund)
            if not ok:
                return Response({"message": "false", "payload": {"error": msg}}, status=400)
            before = _fund_map(fund)
            fund.recycle_bin = True
            fund.is_active = False
            fund.save(update_fields=["recycle_bin", "is_active", "last_updated_on"])
            PettyCashRequest.objects.filter(fund=fund, recycle_bin=False).update(recycle_bin=True)
            _audit(fund.company_branch, staff_profile, "petty_cash_fund", fund.id, "delete", before=before)
            return Response({"message": "true", "payload": {"fund_id": str(fund.id)}})
        return Response({
            "message": "true",
            "payload": {"fund_list": [_fund_map(f) for f in funds]},
        })
    except PettyCashFund.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Fund not found."}}, status=404)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_requests(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        action = request.data.get("action", "list")
        branch_id = request.data.get("branch_id")
        fund_ids = list(_fund_queryset(company_profile, staff_profile, branch_id).values_list("id", flat=True))

        if action == "create":
            fund = PettyCashFund.objects.filter(id__in=fund_ids).get(id=request.data.get("fund_id"))
            req = PettyCashRequest.objects.create(
                fund=fund,
                requested_by=staff_profile,
                purpose=request.data.get("purpose", ""),
                amount=Decimal(str(request.data.get("amount") or 0)),
                status="draft",
            )
            _audit(fund.company_branch, staff_profile, "petty_cash_request", req.id, "create", after=_request_map(req))
            return Response({"message": "true", "payload": {"request": _request_map(req)}})

        if action == "submit":
            req = PettyCashRequest.objects.select_related("fund").get(
                id=request.data.get("request_id"), fund_id__in=fund_ids, recycle_bin=False,
            )
            if req.status != "draft":
                return Response({"message": "false", "payload": {"error": "Only draft requests can be submitted."}}, status=400)
            req.status = "pending_approval"
            req.save(update_fields=["status", "last_updated_on"])
            _audit(req.fund.company_branch, staff_profile, "petty_cash_request", req.id, "submit")
            return Response({"message": "true", "payload": {"request": _request_map(req)}})

        if action == "approve":
            req = PettyCashRequest.objects.select_related("fund").get(
                id=request.data.get("request_id"), fund_id__in=fund_ids, recycle_bin=False,
            )
            if req.status != "pending_approval":
                return Response({"message": "false", "payload": {"error": "Request is not pending approval."}}, status=400)
            req.status = "approved"
            req.approved_by = staff_profile
            req.approved_on = timezone.now()
            req.save(update_fields=["status", "approved_by", "approved_on", "last_updated_on"])
            _audit(req.fund.company_branch, staff_profile, "petty_cash_request", req.id, "approve")
            return Response({"message": "true", "payload": {"request": _request_map(req)}})

        if action == "reject":
            req = PettyCashRequest.objects.select_related("fund").get(
                id=request.data.get("request_id"), fund_id__in=fund_ids, recycle_bin=False,
            )
            if req.status != "pending_approval":
                return Response({"message": "false", "payload": {"error": "Request is not pending approval."}}, status=400)
            req.status = "rejected"
            req.rejection_reason = request.data.get("rejection_reason", "")
            req.approved_by = staff_profile
            req.approved_on = timezone.now()
            req.save(update_fields=["status", "rejection_reason", "approved_by", "approved_on", "last_updated_on"])
            _audit(req.fund.company_branch, staff_profile, "petty_cash_request", req.id, "reject", notes=req.rejection_reason)
            return Response({"message": "true", "payload": {"request": _request_map(req)}})

        status_filter = request.data.get("status_filter", "")
        qs = PettyCashRequest.objects.filter(fund_id__in=fund_ids, recycle_bin=False).select_related(
            "fund", "requested_by", "approved_by",
        ).prefetch_related("disbursement__retirement")
        if status_filter:
            qs = qs.filter(status=status_filter)
        return Response({
            "message": "true",
            "payload": {"request_list": [_request_map(r) for r in qs[:500]]},
        })
    except PettyCashFund.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Fund not found."}}, status=404)
    except PettyCashRequest.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Request not found."}}, status=404)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_disburse(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        fund_ids = list(_fund_queryset(company_profile, staff_profile).values_list("id", flat=True))
        req = PettyCashRequest.objects.select_related(
            "fund", "fund__petty_cash_account", "fund__employee_advances_account",
        ).get(id=request.data.get("request_id"), fund_id__in=fund_ids, recycle_bin=False)
        if req.status != "approved":
            return Response({"message": "false", "payload": {"error": "Request must be approved before disbursement."}}, status=400)
        if hasattr(req, "disbursement"):
            return Response({"message": "false", "payload": {"error": "Request already disbursed."}}, status=400)

        with transaction.atomic():
            disbursement = PettyCashDisbursement.objects.create(
                request=req,
                amount=req.amount,
                disbursed_by=staff_profile,
            )
            post_disbursement(fund=req.fund, disbursement=disbursement, staff=staff_profile, amount=req.amount)
            from .employee_advance import create_advance_from_disbursement
            create_advance_from_disbursement(disbursement=disbursement)
            req.status = "disbursed"
            req.save(update_fields=["status", "last_updated_on"])
            _audit(req.fund.company_branch, staff_profile, "petty_cash_disbursement", disbursement.id, "disburse")

        return Response({
            "message": "true",
            "payload": {
                "request": _request_map(req),
                "disbursement_number": disbursement.disbursement_number,
            },
        })
    except PostingError as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=400)
    except PettyCashRequest.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Request not found."}}, status=404)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_retire(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        fund_ids = list(_fund_queryset(company_profile, staff_profile).values_list("id", flat=True))
        disbursement = PettyCashDisbursement.objects.select_related(
            "request__fund", "request__fund__employee_advances_account",
        ).get(id=request.data.get("disbursement_id"), request__fund_id__in=fund_ids)
        if hasattr(disbursement, "retirement") and disbursement.retirement.status == "posted":
            return Response({"message": "false", "payload": {"error": "Disbursement already retired."}}, status=400)

        lines_payload = request.data.get("lines") or []
        if not lines_payload:
            return Response({"message": "false", "payload": {"error": "Retirement lines are required."}}, status=400)

        with transaction.atomic():
            retirement, _created = PettyCashRetirement.objects.get_or_create(
                disbursement=disbursement,
                defaults={
                    "receipt_reference": request.data.get("receipt_reference", ""),
                    "status": "draft",
                },
            )
            retirement.receipt_reference = request.data.get("receipt_reference", retirement.receipt_reference)
            retirement.lines.all().delete()

            journal_lines = []
            for line in lines_payload:
                account = ChartOfAccount.objects.get(id=line.get("expense_account_id"))
                amount = Decimal(str(line.get("amount") or 0))
                PettyCashRetirementLine.objects.create(
                    retirement=retirement,
                    expense_account=account,
                    description=line.get("description", ""),
                    amount=amount,
                )
                journal_lines.append({"account": account, "amount": amount})

            post_retirement(
                fund=disbursement.request.fund,
                retirement=retirement,
                staff=staff_profile,
                lines=journal_lines,
            )
            from .employee_advance import apply_receipt_settlement
            settled_total = sum(_decimal(line.get("amount")) for line in journal_lines)
            apply_receipt_settlement(disbursement=disbursement, amount=settled_total)
            _audit(
                disbursement.request.fund.company_branch,
                staff_profile,
                "petty_cash_retirement",
                retirement.id,
                "post",
            )

        return Response({
            "message": "true",
            "payload": {"retirement_number": retirement.retirement_number},
        })
    except (ChartOfAccount.DoesNotExist, PettyCashDisbursement.DoesNotExist):
        return Response({"message": "false", "payload": {"error": "Record not found."}}, status=404)
    except PostingError as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=400)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_replenish(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        fund = _fund_queryset(company_profile, staff_profile).get(id=request.data.get("fund_id"))
        amount = Decimal(str(request.data.get("amount") or 0))
        bank_account_id = request.data.get("bank_account_id") or fund.bank_account_id
        bank_account = ChartOfAccount.objects.get(id=bank_account_id)

        with transaction.atomic():
            replenishment = PettyCashReplenishment.objects.create(
                fund=fund,
                amount=amount,
                bank_account=bank_account,
                notes=request.data.get("notes", ""),
            )
            post_replenishment(
                fund=fund,
                replenishment=replenishment,
                staff=staff_profile,
                amount=amount,
                bank_account=bank_account,
            )
            _audit(fund.company_branch, staff_profile, "petty_cash_replenishment", replenishment.id, "post")

        return Response({
            "message": "true",
            "payload": {"replenishment_number": replenishment.replenishment_number},
        })
    except (PettyCashFund.DoesNotExist, ChartOfAccount.DoesNotExist):
        return Response({"message": "false", "payload": {"error": "Record not found."}}, status=404)
    except PostingError as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=400)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_reconcile(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        fund = _fund_queryset(company_profile, staff_profile).get(id=request.data.get("fund_id"))
        period_date = datetime.strptime(request.data.get("period_date"), "%Y-%m-%d").date()
        physical_count = Decimal(str(request.data.get("physical_count") or 0))
        book_balance = fund.current_balance

        recon, _created = PettyCashReconciliation.objects.update_or_create(
            fund=fund,
            period_date=period_date,
            defaults={
                "book_balance": book_balance,
                "physical_count": physical_count,
                "status": "reconciled",
                "notes": request.data.get("notes", ""),
                "reconciled_by": staff_profile,
                "reconciled_on": timezone.now(),
            },
        )
        _audit(fund.company_branch, staff_profile, "petty_cash_reconciliation", recon.id, "reconcile")
        return Response({
            "message": "true",
            "payload": {
                "reconciliation_number": recon.reconciliation_number,
                "variance": str(recon.variance),
            },
        })
    except PettyCashFund.DoesNotExist:
        return Response({"message": "false", "payload": {"error": "Fund not found."}}, status=404)
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_report(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        branch_id = request.data.get("branch_id")
        funds = _fund_queryset(company_profile, staff_profile, branch_id)
        fund_ids = list(funds.values_list("id", flat=True))

        summary_rows = [_fund_map(f) for f in funds]

        advance_rows = []
        disbursements = PettyCashDisbursement.objects.filter(
            request__fund_id__in=fund_ids,
        ).select_related("request", "request__fund", "request__requested_by").prefetch_related("retirement")
        for d in disbursements:
            retirement = getattr(d, "retirement", None)
            if retirement and retirement.status == "posted":
                continue
            advance_rows.append({
                "disbursement_id": str(d.id),
                "disbursement_number": d.disbursement_number,
                "fund_name": d.request.fund.fund_name if d.request and d.request.fund else "",
                "employee_name": _staff_name(d.request.requested_by) if d.request else "",
                "amount": str(d.amount),
                "disbursed_on": d.disbursed_on.strftime("%Y-%m-%d") if d.disbursed_on else "",
                "status": "Outstanding",
            })

        reconciliation_rows = []
        for r in PettyCashReconciliation.objects.filter(fund_id__in=fund_ids).select_related("fund")[:500]:
            reconciliation_rows.append({
                "reconciliation_number": r.reconciliation_number,
                "fund_name": r.fund.fund_name if r.fund else "",
                "period_date": r.period_date.strftime("%Y-%m-%d"),
                "book_balance": str(r.book_balance),
                "physical_count": str(r.physical_count),
                "variance": str(r.variance),
                "status": r.status,
            })

        return Response({
            "message": "true",
            "payload": {
                "summary_list": summary_rows,
                "outstanding_advances_list": advance_rows,
                "reconciliation_list": reconciliation_rows,
            },
        })
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_audit_logs(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        branch_id = request.data.get("branch_id")
        qs = FinanceAuditLog.objects.filter(
            entity_type__startswith="petty_cash",
        ).select_related("actor", "company_branch")
        if branch_id:
            qs = qs.filter(company_branch_id=branch_id)
        elif not check_if_system_administrator(staff_profile) and staff_profile.company_branch_id:
            qs = qs.filter(company_branch_id=staff_profile.company_branch_id)

        rows = []
        for log in qs[:500]:
            rows.append({
                "entity_type": log.entity_type,
                "entity_id": log.entity_id,
                "action": log.action,
                "actor_name": _staff_name(log.actor),
                "branch_name": log.company_branch.branch_name if log.company_branch else "",
                "notes": log.notes,
                "created_on": log.created_on.strftime("%Y-%m-%d %H:%M") if log.created_on else "",
            })
        return Response({"message": "true", "payload": {"audit_log_list": rows}})
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)


@api_view(["POST"])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def petty_cash_workbench(request):
    try:
        company_profile, staff_profile = _auth(request)
        if not _require_finance(staff_profile):
            return Response({"message": "false", "payload": {"error": "Finance access required."}}, status=403)

        fund_ids = list(_fund_queryset(company_profile, staff_profile).values_list("id", flat=True))

        disbursements = PettyCashDisbursement.objects.filter(
            request__fund_id__in=fund_ids,
        ).select_related("request", "request__fund", "request__requested_by")
        disbursement_list = [{
            "disbursement_id": str(d.id),
            "disbursement_number": d.disbursement_number,
            "request_number": d.request.request_number if d.request else "",
            "fund_name": d.request.fund.fund_name if d.request and d.request.fund else "",
            "employee_name": _staff_name(d.request.requested_by) if d.request else "",
            "amount": str(d.amount),
            "disbursed_on": d.disbursed_on.strftime("%Y-%m-%d %H:%M") if d.disbursed_on else "",
        } for d in disbursements[:300]]

        retirements = PettyCashRetirement.objects.filter(
            disbursement__request__fund_id__in=fund_ids,
        ).select_related("disbursement", "disbursement__request")
        retirement_list = [{
            "retirement_number": r.retirement_number,
            "disbursement_number": r.disbursement.disbursement_number if r.disbursement else "",
            "retired_amount": str(r.retired_amount),
            "status": r.status,
            "retired_on": r.retired_on.strftime("%Y-%m-%d %H:%M") if r.retired_on else "",
        } for r in retirements[:300]]

        replenishments = PettyCashReplenishment.objects.filter(
            fund_id__in=fund_ids,
        ).select_related("fund")
        replenishment_list = [{
            "replenishment_number": r.replenishment_number,
            "fund_name": r.fund.fund_name if r.fund else "",
            "amount": str(r.amount),
            "status": r.status,
            "replenished_on": r.replenished_on.strftime("%Y-%m-%d %H:%M") if r.replenished_on else "",
        } for r in replenishments[:300]]

        return Response({
            "message": "true",
            "payload": {
                "disbursement_list": disbursement_list,
                "retirement_list": retirement_list,
                "replenishment_list": replenishment_list,
            },
        })
    except ValueError as exc:
        status = 401 if str(exc) == "staff_profile required" else 400
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=status)
    except Exception as exc:
        return Response({"message": "false", "payload": {"error": str(exc)}}, status=500)
