<?php
namespace App\Infrastructure\Providers;
use App\Domain\Blockchain\Contracts\BlockchainGateway; use App\Domain\Blockchain\Data\{AddressCursor,AddressRequest,ChainTransaction,DepositAddress,FeeQuote,FeeRequest,ProviderTransfer,TransactionPage,TransferRequest,VerifiedProviderEvent}; use Illuminate\Http\Client\PendingRequest; use Illuminate\Support\Facades\Http; use RuntimeException;
final class SelfHostedTronGateway implements BlockchainGateway {
 private function node(): PendingRequest { $request=Http::baseUrl(rtrim((string)config('gateway.tron.node_url'),'/'))->acceptJson()->timeout(10)->retry(2,200,throw:false); $key=(string)config('gateway.tron.api_key'); return $key!=='' ? $request->withHeaders(['TRON-PRO-API-KEY'=>$key]) : $request; }
 private function signer(): PendingRequest { return Http::baseUrl(rtrim((string)config('gateway.tron.signer_url'),'/'))->acceptJson()->withToken((string)config('gateway.tron.signer_token'))->timeout(15); }
 public function createDepositAddress(AddressRequest $r): DepositAddress { $res=$this->signer()->post('/v1/addresses',['network'=>$r->network,'environment'=>$r->environment,'reference'=>$r->invoiceId]); if(!$res->successful()) throw new RuntimeException('PROVIDER_UNAVAILABLE'); return new DepositAddress((string)$res->json('address'),(string)$res->json('reference')); }
 public function validateAddress(string $network,string $address): bool { $res=$this->node()->post('/wallet/validateaddress',['address'=>$address,'visible'=>true]); return $res->successful()&&(bool)$res->json('result'); }
 public function listAddressTransactions(AddressCursor $c): TransactionPage { $res=$this->node()->get('/v1/accounts/'.urlencode($c->address).'/transactions/trc20',array_filter(['limit'=>$c->limit,'fingerprint'=>$c->cursor,'contract_address'=>$c->contractAddress,'only_to'=>true])); if(!$res->successful()) throw new RuntimeException('PROVIDER_UNAVAILABLE'); $txs=[]; foreach((array)$res->json('data',[]) as $i=>$x){$txs[]=new ChainTransaction($c->network,(string)$x['transaction_id'],(string)$x['to'],(string)$x['value'],isset($x['confirmed'])&&$x['confirmed']?1:0,isset($x['confirmed'])&&$x['confirmed']?'confirmed':'unconfirmed',$i,(string)($x['from']??''),(string)($x['token_info']['address']??''),(int)($x['token_info']['decimals']??0),null,new \DateTimeImmutable('@'.intdiv((int)($x['block_timestamp']??0),1000))); } return new TransactionPage($txs,$res->json('meta.fingerprint')); }
 public function getTransaction(string $network,string $txHash): ChainTransaction { $res=$this->node()->post('/wallet/gettransactioninfobyid',['value'=>$txHash]); if(!$res->successful()||!$res->json('id')) throw new RuntimeException('TRANSACTION_NOT_FOUND'); return new ChainTransaction($network,$txHash,'','0',(int)($res->json('blockNumber')?1:0),$res->json('receipt.result')==='SUCCESS'?'confirmed':'failed',0,null,null,null,$res->json('blockNumber')); }
 public function estimateNetworkFee(FeeRequest $r): FeeQuote { return new FeeQuote((string)config('gateway.tron.default_fee_atomic','1000000'),$r->assetCode); }
 public function createTransfer(TransferRequest $r): ProviderTransfer { $payload=['network'=>$r->network,'asset_code'=>$r->assetCode,'to_address'=>$r->toAddress,'amount_atomic'=>$r->amountAtomic,'idempotency_key'=>$r->idempotencyKey]; if($r->fromAddress)$payload['from_address']=$r->fromAddress; $res=$this->signer()->post('/v1/transfers',$payload); if(!$res->successful()) throw new RuntimeException($res->status()>=500?'PROVIDER_UNAVAILABLE':'TRANSFER_REJECTED'); return new ProviderTransfer((string)$res->json('id'),(string)$res->json('status'),$res->json('tx_hash')); }
 public function getTransfer(string $id): ProviderTransfer { $res=$this->signer()->get('/v1/transfers/'.urlencode($id)); if(!$res->successful()) throw new RuntimeException('PROVIDER_UNAVAILABLE'); return new ProviderTransfer((string)$res->json('id'),(string)$res->json('status'),$res->json('tx_hash')); }
 public function verifyWebhook(string $raw,array $headers): VerifiedProviderEvent { $ts=(string)($headers['x-signer-timestamp'][0]??$headers['X-Signer-Timestamp'][0]??''); $sig=(string)($headers['x-signer-signature'][0]??$headers['X-Signer-Signature'][0]??''); if($ts===''||abs(time()-(int)$ts)>300) throw new RuntimeException('INVALID_PROVIDER_SIGNATURE'); $expected='v1='.hash_hmac('sha256',$ts.'.'.$raw,(string)config('gateway.tron.webhook_secret')); if(!hash_equals($expected,$sig)) throw new RuntimeException('INVALID_PROVIDER_SIGNATURE'); $p=json_decode($raw,true,512,JSON_THROW_ON_ERROR); return new VerifiedProviderEvent((string)$p['id'],(string)$p['type'],$p); }
}

