<?php
namespace App\Http\Requests;
use App\Models\Asset;
use App\Support\AtomicAmount;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Validator;
use Throwable;
final class CreatePaymentRequest extends FormRequest {
 public function authorize():bool{return true;}
 public function rules():array{return [
  'price_amount'=>['required','string','regex:/^(?:0|[1-9]\d*)(?:\.\d{1,8})?$/'],
  'price_currency'=>['required','string','in:USD'], 'pay_currency'=>['required','string','max:20'],
  'order_id'=>['required','string','max:100'], 'ipn_callback_url'=>['nullable','url','max:2048'],
  'success_url'=>['nullable','url','max:2048'], 'cancel_url'=>['nullable','url','max:2048'],
  'metadata'=>['nullable','array'],
  'customer_id'=>['nullable','string','max:40'],
  'sub_merchant_id'=>['nullable','string','max:40'],'payer'=>['nullable','array'],'payer.id'=>['nullable','string','max:100'],'payer.name'=>['nullable','string','max:255'],'payer.email'=>['nullable','email','max:255'],
  'items'=>['nullable','array','max:100'],'items.*.sku'=>['nullable','string','max:100'],'items.*.name'=>['required_with:items','string','max:255'],'items.*.description'=>['nullable','string','max:1000'],'items.*.quantity'=>['required_with:items','integer','min:1','max:100000'],'items.*.unit_amount'=>['required_with:items','string','regex:/^(?:0|[1-9]\d*)(?:\.\d{1,2})?$/'],
  'accept_partial'=>['nullable','boolean'],'accept_overpayment'=>['nullable','boolean'],'underpayment_tolerance'=>['nullable','string','regex:/^\d+(?:\.\d{1,6})?$/'],'overpayment_tolerance'=>['nullable','string','regex:/^\d+(?:\.\d{1,6})?$/'],'late_payment_policy'=>['nullable','in:manual_review,accept,reject'],'locale'=>['nullable','string','max:10'],
 ];}
 public function withValidator(Validator $v):void{$v->after(function(Validator $v){
  if(strlen(json_encode($this->input('metadata',[]),JSON_UNESCAPED_SLASHES))>8192)$v->errors()->add('metadata','Metadata may not exceed 8 KB.');
  if($this->depth($this->input('metadata',[]))>5)$v->errors()->add('metadata','Metadata nesting may not exceed 5 levels.');
  foreach(['ipn_callback_url','success_url','cancel_url'] as $field){$url=$this->input($field);if($url&&!$this->safeUrl($url))$v->errors()->add($field,'URL must be HTTPS in live mode and resolve to a public host.');}
  $asset=Asset::where('code',strtoupper((string)$this->input('pay_currency')))->where('enabled',true)->first();
  if($asset){try{$atomic=AtomicAmount::fromDecimal((string)$this->input('price_amount'),$asset->decimals);if($this->compareInts($atomic,(string)$asset->min_amount_atomic)<0)$v->errors()->add('price_amount','Amount is below the configured minimum.');}catch(Throwable){}}
 });}
 private function compareInts(string $a,string $b):int{$a=ltrim($a,'0')?:'0';$b=ltrim($b,'0')?:'0';return strlen($a)<=>strlen($b) ?: strcmp($a,$b);}
 private function depth(mixed $v,int $d=0):int{if(!is_array($v)||$v===[])return $d;$m=$d;foreach($v as $x)$m=max($m,$this->depth($x,$d+1));return $m;}
 private function safeUrl(string $url):bool{$parts=parse_url($url);if(!$parts||empty($parts['host']))return false;$env=$this->attributes->get('environment');if($env==='live'&&strtolower($parts['scheme']??'')!=='https')return false;$host=$parts['host'];if(in_array(strtolower($host),['localhost','localhost.localdomain'],true))return false;$ips=filter_var($host,FILTER_VALIDATE_IP)?[$host]:(gethostbynamel($host)?:[]);foreach($ips as $ip)if(!filter_var($ip,FILTER_VALIDATE_IP,FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE))return false;return $ips!==[];}
}
