"""GL posting helpers for petty cash workflow."""
from __future__ import annotations

from datetime import date
from decimal import Decimal

from django.utils import timezone

from .journal_posting import PostingError
from .models import ManualJournalEntry, PettyCashDisbursement, PettyCashFund, PettyCashReplenishment, PettyCashRetirement
from .posting_engine import post_balanced_lines


def _decimal(value) -> Decimal:
    return Decimal(str(value or 0))


def _require_fund_accounts(fund: PettyCashFund):
    if not fund.petty_cash_account_id:
        raise PostingError("Petty cash GL account is not configured on this fund.")
    if not fund.employee_advances_account_id:
        raise PostingError("Employee advances GL account is not configured on this fund.")



def sync_petty_cash_running_balance(fund: PettyCashFund) -> None:
    """Align legacy ChartOfAccount.running_balance with the fund float."""
    if not fund.petty_cash_account_id:
        return
    fund.petty_cash_account.running_balance = f"{_decimal(fund.current_balance):.2f}"
    fund.petty_cash_account.save(update_fields=["running_balance"])


def post_fund_opening(*, fund: PettyCashFund, staff, amount=None) -> dict:
    """
    Establish opening petty cash float on the GL.
    DR Petty Cash / CR Bank when a bank account is linked, otherwise sync running balance only.
    """
    _require_fund_accounts(fund)
    imprest = _decimal(amount if amount is not None else fund.imprest_amount)
    if imprest <= 0:
        return {}

    result = {}
    bank = fund.bank_account
    if bank:
        result = post_balanced_lines(
            branch=fund.company_branch,
            staff=staff,
            lines=[
                {"account": fund.petty_cash_account, "debit": imprest, "credit": Decimal("0")},
                {"account": bank, "debit": Decimal("0"), "credit": imprest},
            ],
            transaction_date=date.today(),
            description=f"Petty cash fund opening {fund.fund_number}",
            rel_type="petty_cash_fund_opening",
            rel_id=str(fund.id),
            reference=fund.fund_number,
        )

    sync_petty_cash_running_balance(fund)
    return result


def post_disbursement(*, fund: PettyCashFund, disbursement: PettyCashDisbursement, staff, amount) -> dict:
    """
    Cash issuance is NOT expense.
    DR Employee Advances / CR Petty Cash
    """
    _require_fund_accounts(fund)
    amount = _decimal(amount)
    if amount <= 0:
        raise PostingError("Disbursement amount must be positive.")
    if fund.current_balance < amount:
        raise PostingError("Insufficient petty cash balance for disbursement.")

    result = post_balanced_lines(
        branch=fund.company_branch,
        staff=staff,
        lines=[
            {"account": fund.employee_advances_account, "debit": amount, "credit": Decimal("0")},
            {"account": fund.petty_cash_account, "debit": Decimal("0"), "credit": amount},
        ],
        transaction_date=date.today(),
        description=f"Petty cash disbursement {disbursement.disbursement_number}",
        rel_type="petty_cash_disbursement",
        rel_id=str(disbursement.id),
        reference=disbursement.disbursement_number,
    )
    journal_id = result.get("journal_entry_id")
    if journal_id:
        disbursement.journal_entry = ManualJournalEntry.objects.filter(id=journal_id).first()
        disbursement.save(update_fields=["journal_entry"])

    fund.current_balance = _decimal(fund.current_balance) - amount
    fund.save(update_fields=["current_balance", "last_updated_on"])
    sync_petty_cash_running_balance(fund)
    return result


def post_retirement(*, fund: PettyCashFund, retirement: PettyCashRetirement, staff, lines: list[dict]) -> dict:
    """
    Expense recognition on retirement.
    DR Expense account(s) / CR Employee Advances
    """
    _require_fund_accounts(fund)
    if not lines:
        raise PostingError("Retirement requires at least one expense line.")

    journal_lines = []
    total_expense = Decimal("0")
    for line in lines:
        account = line.get("account")
        amount = _decimal(line.get("amount"))
        if not account or amount <= 0:
            continue
        journal_lines.append({"account": account, "debit": amount, "credit": Decimal("0")})
        total_expense += amount

    if total_expense <= 0:
        raise PostingError("Retirement total must be positive.")

    journal_lines.append({
        "account": fund.employee_advances_account,
        "debit": Decimal("0"),
        "credit": total_expense,
    })

    result = post_balanced_lines(
        branch=fund.company_branch,
        staff=staff,
        lines=journal_lines,
        transaction_date=date.today(),
        description=f"Petty cash retirement {retirement.retirement_number}",
        rel_type="petty_cash_retirement",
        rel_id=str(retirement.id),
        reference=retirement.retirement_number,
    )
    journal_id = result.get("journal_entry_id")
    if journal_id:
        retirement.journal_entry = ManualJournalEntry.objects.filter(id=journal_id).first()
        retirement.retired_amount = total_expense
        retirement.status = "posted"
        retirement.retired_by = staff
        retirement.retired_on = timezone.now()
        retirement.save(update_fields=[
            "journal_entry", "retired_amount", "status", "retired_by", "retired_on",
        ])
    return result


def post_replenishment(*, fund: PettyCashFund, replenishment: PettyCashReplenishment, staff, amount, bank_account) -> dict:
    """
    Replenish petty cash from bank.
    DR Petty Cash / CR Bank
    """
    _require_fund_accounts(fund)
    if not bank_account:
        raise PostingError("Bank account is required for replenishment.")
    amount = _decimal(amount)
    if amount <= 0:
        raise PostingError("Replenishment amount must be positive.")

    result = post_balanced_lines(
        branch=fund.company_branch,
        staff=staff,
        lines=[
            {"account": fund.petty_cash_account, "debit": amount, "credit": Decimal("0")},
            {"account": bank_account, "debit": Decimal("0"), "credit": amount},
        ],
        transaction_date=date.today(),
        description=f"Petty cash replenishment {replenishment.replenishment_number}",
        rel_type="petty_cash_replenishment",
        rel_id=str(replenishment.id),
        reference=replenishment.replenishment_number,
    )
    journal_id = result.get("journal_entry_id")
    if journal_id:
        replenishment.journal_entry = ManualJournalEntry.objects.filter(id=journal_id).first()
        replenishment.status = "posted"
        replenishment.replenished_by = staff
        replenishment.replenished_on = timezone.now()
        replenishment.save(update_fields=[
            "journal_entry", "status", "replenished_by", "replenished_on",
        ])

    fund.current_balance = _decimal(fund.current_balance) + amount
    fund.save(update_fields=["current_balance", "last_updated_on"])
    sync_petty_cash_running_balance(fund)
    return result
