<?php

namespace App\Application\Services\Treasury;

use App\Models\Asset;
use App\Models\TreasuryWallet;
use App\Models\WalletAddress;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use RuntimeException;

final class TreasuryWalletService
{
    public function ensureCollectionWallet(Asset $asset, string $environment): TreasuryWallet
    {
        $existing = TreasuryWallet::whereNull('merchant_id')
            ->where('asset_id', $asset->id)
            ->where('environment', $environment)
            ->where('collection_target', true)
            ->first();

        if ($existing && $existing->address) {
            return $existing;
        }

        $address = $this->signerHotWalletAddress();

        return TreasuryWallet::updateOrCreate(
            [
                'merchant_id' => null,
                'asset_id' => $asset->id,
                'environment' => $environment,
                'collection_target' => true,
            ],
            [
                'public_id' => $existing?->public_id ?? 'tw_'.Str::ulid(),
                'provider_wallet_id' => 'signer-hot-wallet',
                'address' => $address,
                'status' => 'active',
                'policy' => [
                    'role' => 'main_collection_wallet',
                    'network' => $asset->network,
                    'asset_code' => $asset->code,
                ],
            ]
        );
    }

    public function syncDepositAddressBalances(Asset $asset, string $environment, int $limit = 100): array
    {
        $addresses = WalletAddress::where('environment', $environment)
            ->where('network', $asset->network)
            ->where('status', 'active')
            ->latest('id')
            ->limit($limit)
            ->get();

        $synced = 0;
        $totalAtomic = '0';

        foreach ($addresses as $address) {
            $balance = $this->trc20BalanceAtomic($address->address, (string) $asset->contract_address);
            $address->update([
                'provider_balance_atomic' => $balance,
                'balance_synced_at' => now(),
            ]);
            $totalAtomic = bcadd($totalAtomic, $balance, 0);
            $synced++;
        }

        return [
            'synced_addresses' => $synced,
            'total_deposit_balance_atomic' => $totalAtomic,
        ];
    }

    public function signerHotWalletAddress(): string
    {
        $signer = rtrim((string) config('gateway.tron.signer_url'), '/');
        $token = (string) config('gateway.tron.signer_token');

        $response = Http::timeout(15)->withToken($token)->get($signer.'/v1/hot-wallet');
        if (! $response->successful()) {
            throw new RuntimeException('SIGNER_HOT_WALLET_UNAVAILABLE');
        }

        $address = (string) $response->json('address', '');
        if ($address === '') {
            $response = Http::timeout(30)->withToken($token)->post($signer.'/v1/hot-wallet');
            if (! $response->successful()) {
                throw new RuntimeException('SIGNER_HOT_WALLET_CREATE_FAILED');
            }
            $address = (string) $response->json('address', '');
        }

        if ($address === '') {
            throw new RuntimeException('SIGNER_HOT_WALLET_EMPTY');
        }

        return $address;
    }

    private function trc20BalanceAtomic(string $address, string $contractAddress): string
    {
        $node = rtrim((string) config('gateway.tron.node_url'), '/');
        $request = Http::baseUrl($node)->acceptJson()->timeout(15);
        $apiKey = (string) config('gateway.tron.api_key');
        if ($apiKey !== '') {
            $request = $request->withHeaders(['TRON-PRO-API-KEY' => $apiKey]);
        }

        $response = $request->get('/v1/accounts/'.urlencode($address).'/transactions/trc20', [
            'limit' => 200,
            'contract_address' => $contractAddress,
        ]);

        if (! $response->successful()) {
            return '0';
        }

        $balance = '0';
        foreach ((array) $response->json('data', []) as $transaction) {
            if (strcasecmp((string) ($transaction['token_info']['address'] ?? ''), $contractAddress) !== 0) {
                continue;
            }

            $value = (string) ($transaction['value'] ?? '0');
            if (strcasecmp((string) ($transaction['to'] ?? ''), $address) === 0) {
                $balance = bcadd($balance, $value, 0);
            }
            if (strcasecmp((string) ($transaction['from'] ?? ''), $address) === 0) {
                $balance = bcsub($balance, $value, 0);
            }
        }

        return bccomp($balance, '0', 0) < 0 ? '0' : $balance;
    }
}
