<?php
namespace App\Application\Services\Blockchain;
use App\Application\Services\{LedgerService,PaymentStateMachine};
use App\Application\Services\Webhooks\MerchantWebhookDispatcher;
use App\Domain\Payments\PaymentStatus;
use App\Models\Invoice;
use App\Support\AtomicAmount;
use Illuminate\Support\Facades\DB;
final class PaymentFinalizer
{
 public function __construct(private LedgerService $ledger,private PaymentStateMachine $states,private MerchantWebhookDispatcher $webhooks){}
 public function finalize(int $invoiceId):void { DB::transaction(function()use($invoiceId){
  $invoice=Invoice::with(['asset','invoiceTransactions.chainTransaction'])->lockForUpdate()->findOrFail($invoiceId); if($invoice->status==='finished')return;
  $policy=$invoice->asset->confirmation_policy??[];$required=(int)($policy['minimum_confirmations']??1);
  $confirmed='0'; foreach($invoice->invoiceTransactions as $link){$tx=$link->chainTransaction;if($tx && $tx->finality_status==='confirmed' && $tx->confirmations>=$required)$confirmed=bcadd($confirmed,(string)$link->allocated_amount_atomic,0);}
  if(bccomp($confirmed,'0',0)<=0)return;
  $expected=(string)$invoice->quoted_pay_amount_atomic; $tolerance=(string)($policy['underpayment_tolerance_atomic']??'0');
  if(bccomp($confirmed,bcsub($expected,$tolerance,0),0)<0){$this->states->transition($invoice,PaymentStatus::Underpaid);return;}
  if(bccomp($confirmed,$expected,0)>0)$this->states->transition($invoice,PaymentStatus::Overpaid);
  $invoice=$invoice->fresh(['asset','invoiceTransactions.chainTransaction']); if(!in_array($invoice->status,['confirming','overpaid','underpaid','manual_review'],true))return;
  $this->states->transition($invoice,PaymentStatus::Confirmed);
  $feeBps=(int)config('gateway.processing_fee_bps',50);$fee=bcdiv(bcmul($confirmed,(string)$feeBps,0),'10000',0);$net=bcsub($confirmed,$fee,0);
  $clearing=$this->ledger->account(null,$invoice->asset->id,'blockchain_clearing');$available=$this->ledger->account($invoice->merchant_id,$invoice->asset->id,'merchant_available');$revenue=$this->ledger->account(null,$invoice->asset->id,'platform_fee_revenue');
  $entries=[['account_id'=>$clearing->id,'direction'=>'debit','amount_atomic'=>$confirmed],['account_id'=>$available->id,'direction'=>'credit','amount_atomic'=>$net]]; if(bccomp($fee,'0',0)>0)$entries[]=['account_id'=>$revenue->id,'direction'=>'credit','amount_atomic'=>$fee];
  $this->ledger->post('invoice',(string)$invoice->public_id,'payment:'.$invoice->public_id,$entries);$invoice=$this->states->transition($invoice->fresh(),PaymentStatus::Finished)->load(['asset','invoiceTransactions.chainTransaction']);
  $txHashes=$invoice->invoiceTransactions->map(fn($l)=>$l->chainTransaction?->tx_hash)->filter()->values()->all();
  $this->webhooks->dispatch($invoice->merchant_id,$invoice->environment,'payment.finished',['payment_id'=>$invoice->public_id,'order_id'=>$invoice->order_id,'status'=>'finished','pay_currency'=>$invoice->asset->code,'pay_amount'=>AtomicAmount::toDecimal((string)$invoice->quoted_pay_amount_atomic,$invoice->asset->decimals),'actually_paid'=>AtomicAmount::toDecimal($confirmed,$invoice->asset->decimals),'fee_amount'=>AtomicAmount::toDecimal($fee,$invoice->asset->decimals),'net_amount'=>AtomicAmount::toDecimal($net,$invoice->asset->decimals),'tx_hashes'=>$txHashes]);
 },5); }
}