<?php

namespace App\Services\Accounting;

use App\Enums\JournalStatus;
use App\Enums\PartyLedger;
use App\Models\JournalLine;
use App\Support\Decimal;

final class LedgerBalanceService
{
    public function customerReceivable(int $partyId): string
    {
        return $this->partyBalance($partyId, PartyLedger::CUSTOMER, true);
    }

    public function vendorPayable(int $partyId): string
    {
        return $this->partyBalance($partyId, PartyLedger::VENDOR, false);
    }

    /** @return array{receivable:string, payable:string, net_exposure:string} */
    public function combinedPartyPosition(int $partyId): array
    {
        $receivable = $this->customerReceivable($partyId);
        $payable = $this->vendorPayable($partyId);

        return [
            'receivable' => $receivable,
            'payable' => $payable,
            'net_exposure' => Decimal::subtract($receivable, $payable),
        ];
    }

    private function partyBalance(
        int $partyId,
        PartyLedger $ledger,
        bool $debitNormal,
    ): string {
        $lines = JournalLine::query()
            ->join('journal_entries', 'journal_entries.id', '=', 'journal_lines.journal_entry_id')
            ->where('journal_entries.status', JournalStatus::POSTED->value)
            ->where('journal_lines.party_id', $partyId)
            ->where('journal_lines.party_ledger', $ledger->value)
            ->get(['journal_lines.debit', 'journal_lines.credit']);

        $debits = Decimal::scale('0');
        $credits = Decimal::scale('0');

        foreach ($lines as $line) {
            $debits = Decimal::add($debits, (string) $line->debit);
            $credits = Decimal::add($credits, (string) $line->credit);
        }

        return $debitNormal
            ? Decimal::subtract($debits, $credits)
            : Decimal::subtract($credits, $debits);
    }
}
