<?php
namespace App\Jobs;
use App\Application\Services\LedgerService;
use App\Models\{LedgerEntry,Payout,SettlementBatch};
use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Foundation\Bus\Dispatchable;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Queue\SerializesModels;use Illuminate\Support\Facades\DB;use Illuminate\Support\Str;use Throwable;
final class CreateSettlementPayoutJob implements ShouldQueue {use Dispatchable,InteractsWithQueue,Queueable,SerializesModels;public int $tries=3;public function __construct(public int $settlementId){$this->onQueue('payouts');}
 public function handle(LedgerService $ledger):void{$s=SettlementBatch::with(['asset','payoutWallet'])->findOrFail($this->settlementId);if($s->payout_id||$s->status==='completed')return;try{$p=DB::transaction(function()use($s,$ledger){$s=SettlementBatch::lockForUpdate()->findOrFail($s->id);if($s->payout_id)return Payout::find($s->payout_id);$available=$ledger->account($s->merchant_id,$s->asset_id,'merchant_available');$reserved=$ledger->account($s->merchant_id,$s->asset_id,'merchant_reserved');$balance=(string)LedgerEntry::where('account_id',$available->id)->lockForUpdate()->selectRaw("COALESCE(SUM(CASE WHEN direction='credit' THEN amount_atomic ELSE -amount_atomic END),0) as balance")->value('balance');if(bccomp($balance,(string)$s->amount_atomic,0)<0)throw new \RuntimeException('INSUFFICIENT_BALANCE');$p=Payout::create(['public_id'=>'po_'.Str::ulid(),'merchant_id'=>$s->merchant_id,'environment'=>$s->environment,'asset_id'=>$s->asset_id,'to_address'=>$s->payoutWallet->address,'amount_atomic'=>(string)$s->amount_atomic,'fee_atomic'=>(string)$s->fee_atomic,'status'=>'pending_screening','idempotency_key'=>'settlement:'.$s->public_id,'two_factor_confirmed_at'=>now()]);$tx=$ledger->post('settlement',$s->public_id,'settlement-reserve:'.$s->public_id,[['account_id'=>$available->id,'direction'=>'debit','amount_atomic'=>(string)$s->amount_atomic],['account_id'=>$reserved->id,'direction'=>'credit','amount_atomic'=>(string)$s->amount_atomic]]);$p->update(['reservation_ledger_transaction_id'=>$tx->id]);$s->update(['payout_id'=>$p->id,'status'=>'pending_screening','approved_at'=>now()]);return $p;},5);ScreenPayoutJob::dispatch($p->id)->onQueue('payouts')->afterCommit();}catch(Throwable $e){$s->update(['status'=>'failed','failure_reason'=>$e->getMessage()]);}}
}