<?php

namespace App\Services\Wallet;

use App\Models\Player;
use Illuminate\Support\Facades\Http;
use GuzzleHttp\Exception\ConnectException;
use App\Services\Integration\PartnerSiteConfigResolver;
use App\Support\Integration\WalletApiUrlSanitizer;

/**
 * 通过 HTTP 调用主站钱包 API（路径见 config lottery.main_site.wallet_*_path）。
 */
final class HttpMainSiteWalletGateway implements MainSiteWalletGateway
{
    public function __construct(
        private readonly PartnerSiteConfigResolver $partnerSiteConfigResolver,
    ) {}

    public function debitMainForLotteryDeposit(
        Player $player,
        string $currencyCode,
        int $amountMinor,
        string $idempotentKey,
    ): MainSiteWalletResult {
        $config = $this->partnerSiteConfigResolver->resolveForPlayer($player);

        return $this->post(
            $config->walletDebitPath,
            $player,
            $currencyCode,
            $amountMinor,
            $idempotentKey,
            $config,
        );
    }

    public function creditMainForLotteryWithdraw(
        Player $player,
        string $currencyCode,
        int $amountMinor,
        string $idempotentKey,
    ): MainSiteWalletResult {
        $config = $this->partnerSiteConfigResolver->resolveForPlayer($player);

        return $this->post(
            $config->walletCreditPath,
            $player,
            $currencyCode,
            $amountMinor,
            $idempotentKey,
            $config,
        );
    }

    public function refundMainForFailedLotteryDeposit(
        Player $player,
        string $currencyCode,
        int $amountMinor,
        string $idempotentKey,
    ): MainSiteWalletResult {
        $config = $this->partnerSiteConfigResolver->resolveForPlayer($player);

        return $this->post(
            $config->walletCreditPath,
            $player,
            $currencyCode,
            $amountMinor,
            $idempotentKey,
            $config,
        );
    }

    private function post(
        string $path,
        Player $player,
        string $currencyCode,
        int $amountMinor,
        string $idempotentKey,
        \App\Services\Integration\PartnerSiteConfig $config,
    ): MainSiteWalletResult {
        if (! $config->hasWalletApi()) {
            $requestSnapshot = [
                'site_code' => $player->site_code,
                'site_player_id' => $player->site_player_id,
                'player_id' => $player->id,
                'currency_code' => $currencyCode,
                'amount_minor' => $amountMinor,
                'idempotent_key' => $idempotentKey,
                '_meta' => ['stub' => true],
            ];

            // 生产环境不允许把“主站未配置钱包 API”当作成功，从而避免资金链路失真。
            if (app()->environment(['production'])) {
                return MainSiteWalletResult::failure(
                    'wallet_api_not_configured',
                    ['stub' => false, 'reason' => 'wallet_api_not_configured'],
                    false,
                    $requestSnapshot,
                );
            }

            // 本地/测试环境允许 stub 成功，方便联调与自动化用例覆盖资金链路流程。
            return MainSiteWalletResult::success(
                null,
                ['stub' => true, 'reason' => 'wallet_api_not_configured'],
                $requestSnapshot,
            );
        }

        $base = WalletApiUrlSanitizer::normalizeAndValidate($config->walletApiUrl);
        if ($base === null) {
            return MainSiteWalletResult::failure(
                'wallet_api_url_invalid',
                ['reason' => 'invalid_base_url'],
                false,
                [
                    'site_code' => $player->site_code,
                    'site_player_id' => $player->site_player_id,
                    'player_id' => $player->id,
                    'currency_code' => $currencyCode,
                    'amount_minor' => $amountMinor,
                    'idempotent_key' => $idempotentKey,
                    '_meta' => ['stub' => true, 'operation' => 'invalid_wallet_api_url'],
                ],
            );
        }

        $requestBody = [
            'site_code' => $player->site_code,
            'site_player_id' => $player->site_player_id,
            'player_id' => $player->id,
            'currency_code' => $currencyCode,
            'amount_minor' => $amountMinor,
            'idempotent_key' => $idempotentKey,
        ];

        $requestSnapshot = array_merge($requestBody, [
            '_meta' => [
                'method' => 'POST',
                'path' => '/'.ltrim($path, '/'),
            ],
        ]);

        $url = $base.'/'.ltrim($path, '/');
        $timeout = $config->walletTimeoutSeconds;
        $apiKey = $config->walletApiKey;

        if (app()->environment(['production'])
            && $config->source === \App\Services\Integration\PartnerSiteConfig::SOURCE_LEGACY_ENV
            && (! is_string($apiKey) || trim($apiKey) === '')
        ) {
            return MainSiteWalletResult::failure(
                'main_site_wallet_api_key_required',
                ['reason' => 'missing_main_site_wallet_api_key'],
                false,
                $requestSnapshot,
            );
        }

        $headers = [];
        if (is_string($apiKey) && $apiKey !== '') {
            $headers['Authorization'] = 'Bearer '.$apiKey;
        }

        try {
            $response = Http::withHeaders($headers)
                ->timeout($timeout)
                ->acceptJson()
                ->asJson()
                ->post($url, $requestBody);
        } catch (\Throwable $e) {
            return MainSiteWalletResult::failure(
                $e->getMessage(),
                null,
                self::isUncertainTransportFailure($e),
                $requestSnapshot,
            );
        }

        $payload = $response->json();
        if (! is_array($payload)) {
            $payload = ['raw' => $response->body()];
        }

        $status = $response->status();
        if ($status === 408 || $status === 504) {
            return MainSiteWalletResult::failure(
                'HTTP '.$status,
                $payload,
                true,
                $requestSnapshot,
            );
        }

        if (! $response->successful()) {
            return MainSiteWalletResult::failure(
                is_string(data_get($payload, 'message'))
                    ? (string) data_get($payload, 'message')
                    : ('HTTP '.$status),
                $payload,
                false,
                $requestSnapshot,
            );
        }

        $ok = (bool) data_get($payload, 'success', true);
        if (! $ok) {
            return MainSiteWalletResult::failure(
                is_string(data_get($payload, 'message'))
                    ? (string) data_get($payload, 'message')
                    : 'main_site_rejected',
                $payload,
                false,
                $requestSnapshot,
            );
        }

        $ref = data_get($payload, 'external_ref_no')
            ?? data_get($payload, 'data.external_ref_no')
            ?? data_get($payload, 'ref');

        return MainSiteWalletResult::success(is_string($ref) ? $ref : null, $payload, $requestSnapshot);
    }

    private static function isUncertainTransportFailure(\Throwable $e): bool
    {
        if ($e instanceof ConnectException) {
            return true;
        }

        $msg = strtolower($e->getMessage());

        return str_contains($msg, 'curl error 28')
            || str_contains($msg, 'connection timed out')
            || str_contains($msg, 'timed out')
            || str_contains($msg, 'operation timed out');
    }
}
