<?php

namespace App\Application\Services\Treasury;

use App\Domain\Blockchain\Contracts\BlockchainGateway;
use App\Domain\Blockchain\Data\TransferRequest;
use App\Models\AddressCollection;
use App\Models\Asset;
use App\Models\TreasuryWallet;
use App\Models\WalletAddress;
use Illuminate\Support\Str;
use RuntimeException;
use Throwable;

final class TreasuryCollectionService
{
    public function __construct(
        private TreasuryWalletService $wallets,
        private BlockchainGateway $gateway,
    ) {
    }

    public function collectAsset(Asset $asset, string $environment, ?int $limit = 25): array
    {
        $treasury = TreasuryWallet::whereNull('merchant_id')
            ->where('asset_id', $asset->id)
            ->where('environment', $environment)
            ->where('collection_target', true)
            ->where('status', 'active')
            ->first();

        if (! $treasury || ! $treasury->address) {
            throw new RuntimeException('MAIN_TREASURY_WALLET_NOT_CONFIGURED');
        }

        $this->wallets->syncDepositAddressBalances($asset, $environment);

        $minimum = (string) config('gateway.treasury.collection_minimum_atomic', '1000000');
        $addresses = WalletAddress::where('environment', $environment)
            ->where('network', $asset->network)
            ->where('status', 'active')
            ->where('provider_balance_atomic', '>=', $minimum)
            ->orderBy('id')
            ->limit($limit ?? 25)
            ->get();

        $summary = ['created' => 0, 'submitted' => 0, 'failed' => 0, 'skipped' => 0, 'collections' => []];

        foreach ($addresses as $address) {
            if (strcasecmp($address->address, $treasury->address) === 0) {
                $summary['skipped']++;
                continue;
            }

            $active = AddressCollection::where('wallet_address_id', $address->id)
                ->whereIn('status', ['requesting', 'submitted', 'confirming', 'failed'])
                ->first();

            if ($active) {
                $summary['skipped']++;
                continue;
            }

            $amount = (string) $address->provider_balance_atomic;
            $collection = AddressCollection::create([
                'public_id' => 'col_'.Str::ulid(),
                'wallet_address_id' => $address->id,
                'asset_id' => $asset->id,
                'environment' => $environment,
                'requested_amount_atomic' => $amount,
                'collected_amount_atomic' => '0',
                'provider_idempotency_key' => 'collection:'.$environment.':'.$address->id.':'.Str::ulid(),
                'status' => 'requesting',
            ]);
            $summary['created']++;

            try {
                $transfer = $this->gateway->createTransfer(new TransferRequest(
                    $asset->network,
                    $asset->code,
                    $treasury->address,
                    $amount,
                    $collection->provider_idempotency_key,
                    $address->address,
                ));

                $collection->update([
                    'provider_reference' => $transfer->providerTransferId,
                    'tx_hash' => $transfer->txHash,
                    'status' => in_array($transfer->status, ['confirmed', 'succeeded'], true) ? 'collected' : 'submitted',
                    'collected_amount_atomic' => in_array($transfer->status, ['confirmed', 'succeeded'], true) ? $amount : '0',
                ]);

                $summary['submitted']++;
            } catch (Throwable $e) {
                $collection->update([
                    'status' => 'failed',
                    'failure_reason' => $e->getMessage(),
                ]);
                $summary['failed']++;
            }

            $summary['collections'][] = $collection->fresh()->only([
                'public_id',
                'wallet_address_id',
                'requested_amount_atomic',
                'collected_amount_atomic',
                'provider_reference',
                'tx_hash',
                'status',
                'failure_reason',
            ]);
        }

        return $summary;
    }

    public function syncSubmittedCollections(string $environment): array
    {
        $summary = ['checked' => 0, 'collected' => 0, 'failed' => 0];

        AddressCollection::with(['address', 'asset'])
            ->where('environment', $environment)
            ->whereIn('status', ['submitted', 'confirming'])
            ->whereNotNull('provider_reference')
            ->orderBy('id')
            ->chunkById(50, function ($collections) use (&$summary) {
                foreach ($collections as $collection) {
                    $summary['checked']++;
                    try {
                        $transfer = $this->gateway->getTransfer((string) $collection->provider_reference);
                        if (in_array($transfer->status, ['confirmed', 'succeeded'], true)) {
                            $collection->update([
                                'status' => 'collected',
                                'collected_amount_atomic' => $collection->requested_amount_atomic,
                                'tx_hash' => $transfer->txHash ?? $collection->tx_hash,
                            ]);
                            $collection->address?->update(['last_collected_at' => now()]);
                            $summary['collected']++;
                        } elseif ($transfer->status === 'failed') {
                            $collection->update(['status' => 'failed', 'failure_reason' => 'Provider transfer failed.']);
                            $summary['failed']++;
                        }
                    } catch (Throwable $e) {
                        $collection->update(['failure_reason' => $e->getMessage()]);
                        $summary['failed']++;
                    }
                }
            });

        return $summary;
    }
}

