<?php
namespace App\Application\Services;
use App\Models\LedgerAccount;
use App\Models\LedgerTransaction;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class LedgerService {
 public function post(string $referenceType,string $referenceId,string $idempotencyKey,array $entries):LedgerTransaction {
  return DB::transaction(function()use($referenceType,$referenceId,$idempotencyKey,$entries){
   $existing=LedgerTransaction::where('idempotency_key',$idempotencyKey)->first(); if($existing)return $existing;
   $debits='0';$credits='0'; foreach($entries as $e){ if(!in_array($e['direction'],['debit','credit'],true)||bccomp($e['amount_atomic'],'0',0)<=0) throw ValidationException::withMessages(['entries'=>'Ledger entries must be positive debits or credits.']); if($e['direction']==='debit')$debits=bcadd($debits,$e['amount_atomic'],0);else$credits=bcadd($credits,$e['amount_atomic'],0); }
   if(bccomp($debits,$credits,0)!==0) throw ValidationException::withMessages(['entries'=>'Ledger transaction is not balanced.']);
   $tx=LedgerTransaction::create(['public_id'=>(string)\Illuminate\Support\Str::ulid(),'reference_type'=>$referenceType,'reference_id'=>$referenceId,'idempotency_key'=>$idempotencyKey,'posted_at'=>now()]);
   foreach($entries as $e)$tx->entries()->create($e); return $tx->load('entries');
  },5);
 }
 public function account(?int $merchantId,int $assetId,string $type):LedgerAccount { return LedgerAccount::firstOrCreate(['merchant_id'=>$merchantId,'asset_id'=>$assetId,'type'=>$type],['status'=>'active']); }
 public function merchantBalances(int $merchantId):array { return DB::table('ledger_entries as le')->join('ledger_accounts as la','la.id','=','le.account_id')->where('la.merchant_id',$merchantId)->select('la.asset_id','la.type',DB::raw("CAST(SUM(CASE WHEN le.direction='credit' THEN le.amount_atomic ELSE -le.amount_atomic END) AS CHAR) AS balance_atomic"))->groupBy('la.asset_id','la.type')->get()->groupBy('asset_id')->map(fn($rows)=>$rows->pluck('balance_atomic','type'))->all(); }
}
