<?php

declare(strict_types=1);

require_once __DIR__.'/../../vendor/autoload.php';
$app = require __DIR__.'/../../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

use App\Application\Services\LedgerService;
use App\Jobs\CreateSettlementPayoutJob;
use App\Jobs\DeliverMerchantWebhookJob;
use App\Jobs\PollOpenInvoiceAddressJob;
use App\Jobs\ScreenPayoutJob;
use App\Models\Asset;
use App\Models\Invoice;
use App\Models\Merchant;
use App\Models\Payout;
use App\Models\SettlementBatch;


$baseUrl = 'http://127.0.0.1:8001';
$signerUrl = 'http://127.0.0.1:8787';
$merchantEmail = 'ahmadshahzaib2490+3@gmail.com';
$merchantPassword = 'waqas123waqas';
$adminEmail = 'admin@prolificex.local';
$adminPassword = 'Admin12345678';
$results = [];
$state = [];

function add_result(array &$results, string $group, string $name, string $method, string $url, array $response, array $expect = [200,201,202,204], ?string $note = null): void {
    $status = (int)($response['status'] ?? 0);
    $ok = in_array($status, $expect, true);
    $body = $response['json'] ?? $response['body'] ?? null;
    $error = null;
    if (!$ok) {
        if (is_array($body) && isset($body['error']['code'])) $error = $body['error']['code'].': '.($body['error']['message'] ?? '');
        elseif (is_string($body)) $error = substr($body, 0, 500);
        else $error = $response['error'] ?? 'Unexpected status';
    }
    $results[] = compact('group','name','method','url','status','ok','expect','error','note');
}
function req(string $method,string $url,array $headers=[],mixed $body=null):array{
    $raw = $body === null ? '' : (is_string($body) ? $body : json_encode($body, JSON_UNESCAPED_SLASHES));
    $headers[]='Accept: application/json';
    if($raw!=='' && !array_filter($headers, fn($h)=>stripos($h,'Content-Type:')===0)) $headers[]='Content-Type: application/json';
    $ch=curl_init($url);curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_HEADER=>true,CURLOPT_CUSTOMREQUEST=>$method,CURLOPT_TIMEOUT=>45]);
    if($raw!=='') curl_setopt($ch,CURLOPT_POSTFIELDS,$raw);
    curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
    $res=curl_exec($ch);$err=curl_error($ch);$status=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);$hs=curl_getinfo($ch,CURLINFO_HEADER_SIZE);curl_close($ch);
    $text=$res===false?'':substr($res,$hs);$json=json_decode($text,true);
    return ['status'=>$status,'body'=>$text,'json'=>json_last_error()===JSON_ERROR_NONE?$json:null,'error'=>$err];
}
function bearer(string $token):array{return ['Authorization: Bearer '.$token];}
function path_canon(string $path):string{$parts=parse_url($path);$p=$parts['path']??$path;if(empty($parts['query']))return $p;parse_str($parts['query'],$q);$sort=function(&$v)use(&$sort){if(!is_array($v))return;ksort($v,SORT_STRING);foreach($v as &$i)$sort($i);};$sort($q);return $p.'?'.http_build_query($q,'','&',PHP_QUERY_RFC3986);} 
function hmac_req(string $method,string $base,string $path,string $key,string $secret,mixed $body=null,array $extra=[]):array{$raw=$body===null?'':(is_string($body)?$body:json_encode($body,JSON_UNESCAPED_SLASHES));$ts=(string)time();$nonce=bin2hex(random_bytes(12));$canonical=implode("\n",[strtoupper($method),path_canon($path),$ts,$nonce,hash('sha256',$raw)]);$sig=hash_hmac('sha256',$canonical,$secret);$headers=array_merge($extra,['x-api-key: '.$key,'x-api-timestamp: '.$ts,'x-api-nonce: '.$nonce,'x-api-signature: v1='.$sig]);return req($method,$base.$path,$headers,$raw);} 
function getv(?array $a,string $path,mixed $default=null):mixed{$v=$a;foreach(explode('.',$path) as $s){if(!is_array($v)||!array_key_exists($s,$v))return $default;$v=$v[$s];}return $v;}
function signer_token():?string{$path=__DIR__.'/../../signer/.env';if(!is_file($path))return null;foreach(file($path,FILE_IGNORE_NEW_LINES)?:[] as $line){if(str_starts_with($line,'TRON_SIGNER_TOKEN='))return trim(substr($line,18));}return null;}
function multipart_req(string $url,array $headers,array $fields):array{$headers[]='Accept: application/json';$ch=curl_init($url);curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_HEADER=>true,CURLOPT_POST=>true,CURLOPT_TIMEOUT=>45,CURLOPT_POSTFIELDS=>$fields,CURLOPT_HTTPHEADER=>$headers]);$res=curl_exec($ch);$err=curl_error($ch);$status=curl_getinfo($ch,CURLINFO_RESPONSE_CODE);$hs=curl_getinfo($ch,CURLINFO_HEADER_SIZE);curl_close($ch);$text=$res===false?'':substr($res,$hs);$json=json_decode($text,true);return ['status'=>$status,'body'=>$text,'json'=>json_last_error()===JSON_ERROR_NONE?$json:null,'error'=>$err];}

// Health
$r=req('GET',$baseUrl.'/api/health/ready');add_result($results,'Health','Laravel ready','GET','/api/health/ready',$r);
$r=req('GET',$signerUrl.'/health');add_result($results,'Health','Signer ready','GET','/health',$r);

// Auth
$r=req('POST',$baseUrl.'/api/v1/auth/login',[],['email'=>$merchantEmail,'password'=>$merchantPassword]);add_result($results,'Auth','Merchant login','POST','/api/v1/auth/login',$r);$merchantToken=(string)getv($r['json'],'data.token','');
$r=req('POST',$baseUrl.'/api/v1/auth/login',[],['email'=>$adminEmail,'password'=>$adminPassword]);add_result($results,'Auth','Admin login','POST','/api/v1/auth/login',$r);$adminToken=(string)getv($r['json'],'data.token','');

// API key
$r=req('POST',$baseUrl.'/api/v1/api-keys',bearer($merchantToken),['environment'=>'sandbox','scopes'=>['payments:read','payments:write','balances:read','payouts:read','payouts:write','webhooks:write','merchants:read','merchants:write','wallets:read','wallets:write']]);add_result($results,'Auth','Create HMAC API key','POST','/api/v1/api-keys',$r);$apiKey=(string)getv($r['json'],'data.public_key','');$apiSecret=(string)getv($r['json'],'data.secret','');

// Generate valid TRON payout address from signer
$token=signer_token();$generatedAddress='';
if($token){$r=req('POST',$signerUrl.'/v1/addresses',['Authorization: Bearer '.$token],['network'=>'tron','environment'=>'sandbox','reference'=>'smoke-payout-wallet-'.time()]);add_result($results,'Signer','Generate valid TRON address','POST','/v1/addresses',$r);$generatedAddress=(string)getv($r['json'],'address','');}else{add_result($results,'Signer','Generate valid TRON address','POST','/v1/addresses',['status'=>0,'body'=>'missing signer token'],[200],'Blocked: signer token missing');}

// Payout wallet management
$payoutWalletId='';
if($apiKey && $generatedAddress){$r=hmac_req('POST',$baseUrl,'/api/v1/payout-wallets',$apiKey,$apiSecret,['pay_currency'=>'USDTTRC20','address'=>$generatedAddress,'label'=>'Smoke Test Wallet','is_default'=>true,'metadata'=>['source'=>'latest_new_api_smoke']]);add_result($results,'Payout wallets','Create payout wallet','POST','/api/v1/payout-wallets',$r);$payoutWalletId=(string)getv($r['json'],'data.id','');$r=hmac_req('GET',$baseUrl,'/api/v1/payout-wallets',$apiKey,$apiSecret);add_result($results,'Payout wallets','List payout wallets','GET','/api/v1/payout-wallets',$r);} 

// KYC upload and operator review
$tmpPdf=__DIR__.'/kyc_smoke.pdf';file_put_contents($tmpPdf,"%PDF-1.4\n1 0 obj <<>> endobj\ntrailer <<>>\n%%EOF");
$r=multipart_req($baseUrl.'/api/v1/kyc/documents',bearer($merchantToken),['type'=>'identity','metadata[source]'=>'latest_new_api_smoke','file'=>new CURLFile($tmpPdf,'application/pdf','kyc-smoke.pdf')]);add_result($results,'KYC','Upload KYC document','POST','/api/v1/kyc/documents',$r);$kycId=(string)getv($r['json'],'data.id','');
if($kycId && $adminToken){$r=req('POST',$baseUrl.'/api/v1/operator/kyc/documents/'.$kycId.'/approve',bearer($adminToken),[]);add_result($results,'KYC','Operator approve KYC','POST','/api/v1/operator/kyc/documents/{id}/approve',$r);} 

// Webhook endpoint + delivery to public HTTPS echo
$webhookDeliveryNote=null;$deliveryStatus=null;
if($merchantToken){$r=req('POST',$baseUrl.'/api/v1/webhook-endpoints',bearer($merchantToken),['url'=>'https://httpbin.org/post','environment'=>'sandbox','events'=>['payment.*','payment.test']]);add_result($results,'Webhooks','Create HTTPS webhook endpoint','POST','/api/v1/webhook-endpoints',$r,[201,409,422]);
 if($apiKey){$r=hmac_req('POST',$baseUrl,'/api/v1/webhooks/test',$apiKey,$apiSecret,['event'=>'payment.test']);add_result($results,'Webhooks','Queue webhook test event','POST','/api/v1/webhooks/test',$r,[202,200]);$merchantModel=Merchant::where('email',$merchantEmail)->first();$endpoint=$merchantModel?\App\Models\WebhookEndpoint::where('merchant_id',$merchantModel->id)->where('environment','sandbox')->where('url','https://httpbin.org/post')->latest('id')->first():null;if($endpoint){$eventId='evt_'.\Illuminate\Support\Str::ulid();$payload=['id'=>$eventId,'type'=>'payment.test','created_at'=>now()->utc()->toIso8601String(),'test_mode'=>true,'data'=>['message'=>'Direct signed delivery smoke test']];$raw=json_encode($payload,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR);$delivery=\App\Models\WebhookDelivery::create(['event_id'=>$eventId,'endpoint_id'=>$endpoint->id,'payload'=>$payload,'raw_payload'=>$raw,'attempt_count'=>0,'status'=>'queued','next_attempt_at'=>now()]);try{app(\App\Jobs\DeliverMerchantWebhookJob::class,['deliveryId'=>$delivery->id])->handle(app(App\Application\Services\Webhooks\WebhookRetryPolicy::class),app(App\Application\Services\Webhooks\WebhookDestinationValidator::class));$delivery->refresh();add_result($results,'Webhooks','Deliver signed webhook to https://httpbin.org/post','JOB','DeliverMerchantWebhookJob',['status'=>$delivery->status==='delivered'?200:599,'body'=>$delivery->last_error,'json'=>['status'=>$delivery->status,'response_code'=>$delivery->last_response_code]],[200],$delivery->status==='delivered'?'Signed delivery accepted':'Delivery attempted but not accepted/reachable');}catch(Throwable $e){add_result($results,'Webhooks','Deliver signed webhook to https://httpbin.org/post','JOB','DeliverMerchantWebhookJob',['status'=>599,'body'=>$e->getMessage()],[200],'Blocked by external network/DNS/SSL or destination validation');}}}
}

// Create invoice and run deposit monitor job. No real testnet transfer is sent here.
$invoicePublic='';
if($apiKey){$order='smoke-deposit-'.time();$r=hmac_req('POST',$baseUrl,'/api/v1/invoices',$apiKey,$apiSecret,['price_amount'=>'1.00','price_currency'=>'USD','pay_currency'=>'USDTTRC20','order_id'=>$order,'metadata'=>['source'=>'deposit_monitor_test']],['Idempotency-Key: invoice-'.$order]);add_result($results,'Deposits','Create invoice for deposit monitor','POST','/api/v1/invoices',$r);$invoicePublic=(string)getv($r['json'],'data.invoice_id','');if($invoicePublic){$invoice=Invoice::where('public_id',$invoicePublic)->first();try{app(PollOpenInvoiceAddressJob::class,['invoiceId'=>$invoice->id])->handle(app(App\Domain\Blockchain\Contracts\BlockchainGateway::class),app(App\Application\Services\Blockchain\Trc20TransactionProcessor::class));add_result($results,'Deposits','Run TRON deposit monitor for invoice','JOB','PollOpenInvoiceAddressJob',['status'=>200,'body'=>'completed'],[200],'No transfer was sent, so status should remain waiting unless address already had TRC20 activity.');}catch(Throwable $e){add_result($results,'Deposits','Run TRON deposit monitor for invoice','JOB','PollOpenInvoiceAddressJob',['status'=>599,'body'=>$e->getMessage()],[200],'Provider/network issue while reading TronGrid');}}}

// Create sandbox merchant balance and settlement
if($apiKey && $payoutWalletId){$merchant=Merchant::where('email',$merchantEmail)->first();$asset=Asset::where('code','USDTTRC20')->first();if($merchant && $asset){$ledger=app(LedgerService::class);$amount='25000000';$available=$ledger->account($merchant->id,$asset->id,'merchant_available');$clearing=$ledger->account(null,$asset->id,'blockchain_clearing');try{$ledger->post('test_credit',(string)$merchant->id,'latest-smoke-credit-'.time(),[['account_id'=>$clearing->id,'direction'=>'debit','amount_atomic'=>$amount],['account_id'=>$available->id,'direction'=>'credit','amount_atomic'=>$amount]]);add_result($results,'Settlements','Create sandbox available balance','DB','LedgerService::post',['status'=>200,'body'=>'credited 25 USDTTRC20']);}catch(Throwable $e){add_result($results,'Settlements','Create sandbox available balance','DB','LedgerService::post',['status'=>599,'body'=>$e->getMessage()],[200]);}
$r=hmac_req('POST',$baseUrl,'/api/v1/settlements',$apiKey,$apiSecret,['pay_currency'=>'USDTTRC20','payout_wallet_id'=>$payoutWalletId,'amount'=>'5.00']);add_result($results,'Settlements','Create settlement from merchant balance','POST','/api/v1/settlements',$r,[202]);$settlementId=(string)getv($r['json'],'data.id','');if($settlementId){$settlement=SettlementBatch::where('public_id',$settlementId)->first();try{app(CreateSettlementPayoutJob::class,['settlementId'=>$settlement->id])->handle(app(LedgerService::class));$settlement->refresh();$payout=$settlement->payout_id?Payout::find($settlement->payout_id):null;add_result($results,'Settlements','Create payout from settlement job','JOB','CreateSettlementPayoutJob',['status'=>$payout?200:599,'body'=>$payout?('payout '.$payout->public_id.' '.$payout->status):'no payout'],[200],'Payout is created but not broadcast.');if($payout){try{app(ScreenPayoutJob::class,['payoutId'=>$payout->id])->handle(app(App\Application\Services\Payouts\PayoutPolicyService::class),app(App\Application\Services\Payouts\PayoutStateMachine::class),app(App\Application\Services\Payouts\PayoutSettlementService::class),app(App\Application\Services\Risk\ManualRiskScreeningService::class));$payout->refresh();add_result($results,'Payouts','Screen settlement payout to approval boundary','JOB','ScreenPayoutJob',['status'=>in_array($payout->status,['pending_approval','manual_review','failed'],true)?200:599,'body'=>$payout->status],[200],'Not approved/broadcast to avoid moving funds.');}catch(Throwable $e){add_result($results,'Payouts','Screen settlement payout to approval boundary','JOB','ScreenPayoutJob',['status'=>599,'body'=>$e->getMessage()],[200]);}}}catch(Throwable $e){add_result($results,'Settlements','Create payout from settlement job','JOB','CreateSettlementPayoutJob',['status'=>599,'body'=>$e->getMessage()],[200]);}}}}

// Actual payout broadcast: intentionally skipped unless user confirms testnet/non-funded wallet.
add_result($results,'Payouts','Actual payout broadcast','POST','/api/v1/operator/payouts/{id}/approve',['status'=>299,'body'=>'Skipped intentionally'],[299],'Skipped: this can move real TRON/USDT. Provide testnet signer config/funds to execute safely.');

$passed=count(array_filter($results,fn($r)=>$r['ok']));$failed=count($results)-$passed;$stamp=date('Ymd_His');$md="# Latest New API Targeted Test Report\n\nStarted: ".date('c')."\n\nPassed: {$passed}\nFailed/Blocked: {$failed}\n\n| # | Group | Test | Method | Endpoint | Result | Status | Note/Error |\n|---:|---|---|---|---|---|---:|---|\n";foreach($results as $i=>$r){$note=$r['note'] ?: ($r['error'] ?: '');$md.='| '.($i+1).' | '.$r['group'].' | '.$r['name'].' | `'.$r['method'].'` | `'.$r['url'].'` | '.($r['ok']?'PASS':'CHECK').' | '.$r['status'].' | '.str_replace(["\r","\n",'|'],[' ',' ','/'],$note).' |' . "\n";}
$mdPath=__DIR__.'/latest_new_api_test_report_'.$stamp.'.md';$jsonPath=__DIR__.'/latest_new_api_test_report_'.$stamp.'.json';file_put_contents($mdPath,$md);file_put_contents($jsonPath,json_encode(['passed'=>$passed,'failed_or_blocked'=>$failed,'results'=>$results],JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));echo $md;echo "\nMD_REPORT={$mdPath}\nJSON_REPORT={$jsonPath}\n";