<?php

namespace App\Services;

use Illuminate\Http\Client\Pool;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class ThirdGameBalanceService
{
    private const CODE_SUCCESS = 10000;

    /**
     * 批量查询用户的三方游戏总余额，三方与主钱包金额按 1:1 处理。
     *
     * @param array<int, string|int> $memberIds
     * @return array<string, float|null>
     */
    public function totals(array $memberIds): array
    {
        $memberIds = array_values(array_unique(array_map('strval', $memberIds)));
        $totals = [];
        $pending = [];

        foreach ($memberIds as $memberId) {
            $cached = Cache::get($this->cacheKey($memberId));
            if ($cached !== null) {
                $totals[$memberId] = (float) $cached;
            } else {
                $pending[] = $memberId;
            }
        }

        if ($pending === []) {
            return $totals;
        }

        if (!$this->configured()) {
            Log::warning('third_game_balance_config_missing');
            foreach ($pending as $memberId) {
                $totals[$memberId] = null;
            }
            return $totals;
        }

        try {
            $responses = Http::pool(function (Pool $pool) use ($pending) {
                $requests = [];
                foreach ($pending as $memberId) {
                    $random = $this->random();
                    $requests[] = $pool->as($memberId)
                        ->asJson()
                        ->withHeaders([
                            'sign' => md5($random . config('third_game.sn') . config('third_game.key')),
                            'random' => $random,
                            'sn' => config('third_game.sn'),
                        ])
                        ->timeout(max(1, (int) config('third_game.timeout', 10)))
                        ->connectTimeout(5)
                        ->withOptions($this->httpOptions())
                        ->post(config('third_game.api_url') . '/api/server/balanceAll', [
                            'playerId' => $this->playerId($memberId),
                            'currency' => config('third_game.currency', 'CNY'),
                        ]);
                }
                return $requests;
            });
        } catch (\Throwable $e) {
            Log::warning('third_game_balance_pool_failed', ['message' => $e->getMessage()]);
            foreach ($pending as $memberId) {
                $totals[$memberId] = null;
            }
            return $totals;
        }

        foreach ($pending as $memberId) {
            $response = $responses[$memberId] ?? null;
            $balances = $this->balancesFromResponse($response);
            $total = $balances === null ? null : $this->totalFromBalances($balances);
            $totals[$memberId] = $total;
            if ($total !== null) {
                Cache::put(
                    $this->cacheKey($memberId),
                    $total,
                    max(1, (int) config('third_game.cache_seconds', 30))
                );
                Cache::put(
                    $this->detailCacheKey($memberId),
                    $balances,
                    max(1, (int) config('third_game.cache_seconds', 30))
                );
            }
        }

        return $totals;
    }

    public function playerId(string $memberId): string
    {
        return 'p' . substr(md5(config('third_game.sn') . '_' . $memberId), 0, 10);
    }

    /**
     * 查询单个用户在 ag、pg 等各游戏平台的余额。
     */
    public function detail(string $memberId, bool $forceRefresh = false): array
    {
        if (!$forceRefresh) {
            $cached = Cache::get($this->detailCacheKey($memberId));
            if (is_array($cached)) {
                return $this->detailResult($memberId, $cached);
            }
        }

        $response = $this->request('/api/server/balanceAll', [
            'playerId' => $this->playerId($memberId),
            'currency' => config('third_game.currency', 'CNY'),
        ]);
        $balances = $this->balancesFromResponse($response);
        if ($balances === null) {
            return ['ok' => false, 'msg' => $this->responseMessage($response)];
        }

        $ttl = max(1, (int) config('third_game.cache_seconds', 30));
        Cache::put($this->detailCacheKey($memberId), $balances, $ttl);
        Cache::put($this->cacheKey($memberId), $this->totalFromBalances($balances), $ttl);
        return $this->detailResult($memberId, $balances);
    }

    /**
     * 一键把用户在所有三方游戏平台的余额转出。
     */
    public function recycle(string $memberId): array
    {
        // 必须在调用 transferAll 前校验，避免三方已转出后才因本地配置错误中断。
        $this->recycleRate();

        $response = $this->request('/api/server/transferAll', [
            'playerId' => $this->playerId($memberId),
            'currency' => config('third_game.currency', 'CNY'),
        ], true);

        $body = $response instanceof Response ? $response->json() : null;
        if (!is_array($body)) {
            return [
                'ok' => false,
                'uncertain' => true,
                'msg' => $response instanceof Response
                    ? '三方游戏接口返回异常，回收结果待核对'
                    : '三方游戏接口请求失败，回收结果待核对',
            ];
        }

        if ((int) ($body['code'] ?? 0) === self::CODE_SUCCESS) {
            $data = $body['data'] ?? null;
            if (!is_array($data)
                || !array_key_exists('balanceAll', $data)
                || !is_numeric($data['balanceAll'])
                || (float) $data['balanceAll'] < 0) {
                return [
                    'ok' => false,
                    'uncertain' => true,
                    'msg' => '三方游戏回收成功响应缺少有效 balanceAll，回收结果待核对',
                ];
            }
            $gameBalance = $this->decimalAmount($data['balanceAll']);
            if ($gameBalance === null) {
                return [
                    'ok' => false,
                    'uncertain' => true,
                    'msg' => '三方游戏回收金额格式异常，回收结果待核对',
                ];
            }
            $this->clearCache($memberId);
            return [
                'ok' => true,
                'game_balance' => $gameBalance,
                'wallet_balance' => $this->toWalletBalance($gameBalance),
            ];
        }

        if ($this->isPlayerNotFound((string) ($body['msg'] ?? ''))) {
            $this->clearCache($memberId);
            return ['ok' => true, 'game_balance' => '0.0000000000', 'wallet_balance' => '0.0000000000'];
        }

        return [
            'ok' => false,
            'uncertain' => false,
            'msg' => $this->responseMessage($response),
        ];
    }

    public function toWalletBalance($gameBalance): string
    {
        $balance = $this->decimalAmount($gameBalance);
        if ($balance === null) {
            throw new \InvalidArgumentException('三方游戏余额格式异常');
        }

        return bcdiv($balance, $this->recycleRate(), 10);
    }

    private function recycleRate(): string
    {
        $rate = trim((string) config('third_game.recycle_rate', '1'));
        if (!preg_match('/^\d+(?:\.\d+)?$/D', $rate) || bccomp($rate, '0', 10) <= 0) {
            throw new \RuntimeException('GAME_RECYCLE_RATE 必须是大于 0 的数字');
        }

        return $rate;
    }

    private function balancesFromResponse($response): ?array
    {
        if (!$response instanceof Response) {
            return null;
        }

        $body = $response->json();
        if (!is_array($body)) {
            return null;
        }

        if ((int) ($body['code'] ?? 0) === self::CODE_SUCCESS) {
            return is_array($body['data'] ?? null) ? $body['data'] : [];
        }

        if ($this->isPlayerNotFound((string) ($body['msg'] ?? ''))) {
            return [];
        }

        Log::warning('third_game_balance_query_failed', [
            'code' => $body['code'] ?? null,
            'msg' => $body['msg'] ?? null,
        ]);
        return null;
    }

    private function detailResult(string $memberId, array $balances): array
    {
        $gameBalance = $this->sumBalances($balances);
        $walletBalances = [];
        $gameBalances = [];
        foreach ($balances as $platform => $balance) {
            if (is_numeric($balance)) {
                $walletBalances[] = [
                    'platform' => (string) $platform,
                    'balance' => (float) $balance,
                ];
                $gameBalances[] = [
                    'platform' => (string) $platform,
                    'balance' => (float) $balance,
                ];
            }
        }
        return [
            'ok' => true,
            'member_id' => $memberId,
            'player_id' => $this->playerId($memberId),
            'currency' => (string) config('third_game.currency', 'CNY'),
            'rate' => 1,
            'list' => $walletBalances,
            'game_list' => $gameBalances,
            'total_game_balance' => (float) $gameBalance,
            'total_balance' => $this->totalFromBalances($balances),
        ];
    }

    private function totalFromBalances(array $balances): float
    {
        return (float) $this->sumBalances($balances);
    }

    private function sumBalances(array $balances): string
    {
        $total = '0.0000000000';
        foreach ($balances as $balance) {
            $amount = $this->decimalAmount($balance);
            if ($amount !== null) {
                $total = bcadd($total, $amount, 10);
            }
        }
        return $total;
    }

    private function decimalAmount($value): ?string
    {
        if (!is_int($value) && !is_float($value) && !is_string($value)) {
            return null;
        }

        $value = trim((string) $value);
        if (!preg_match('/^\d+(?:\.\d+)?$/D', $value)) {
            return null;
        }
        return bcadd($value, '0', 10);
    }

    private function request(string $path, array $data, bool $transfer = false)
    {
        if (!$this->configured()) {
            return null;
        }

        $random = $this->random();
        try {
            return Http::asJson()
                ->withHeaders([
                    'sign' => md5($random . config('third_game.sn') . config('third_game.key')),
                    'random' => $random,
                    'sn' => config('third_game.sn'),
                ])
                ->timeout(max(1, (int) config(
                    $transfer ? 'third_game.transfer_timeout' : 'third_game.timeout',
                    $transfer ? 65 : 10
                )))
                ->connectTimeout(5)
                ->withOptions($this->httpOptions())
                ->post(config('third_game.api_url') . $path, $data);
        } catch (\Throwable $e) {
            Log::warning('third_game_request_failed', [
                'path' => $path,
                'message' => $e->getMessage(),
            ]);
            return null;
        }
    }

    private function responseMessage($response): string
    {
        if (!$response instanceof Response) {
            return $this->configured() ? '三方游戏接口请求失败' : '三方游戏配置缺失';
        }
        return (string) ($response->json('msg') ?: '三方游戏接口异常');
    }

    private function configured(): bool
    {
        return config('third_game.api_url') !== ''
            && config('third_game.sn') !== ''
            && config('third_game.key') !== '';
    }

    private function isPlayerNotFound(string $message): bool
    {
        $message = strtolower($message);
        return str_contains($message, 'playerid')
            && (str_contains($message, '不存在')
                || str_contains($message, 'not exist')
                || str_contains($message, 'not found'));
    }

    private function httpOptions(): array
    {
        $options = [];
        $proxy = (string) config('third_game.proxy', '');
        if ($proxy !== '') {
            $options['proxy'] = $proxy;
        }
        $caBundle = (string) config('third_game.ca_bundle', '');
        if ($caBundle !== '') {
            $options['verify'] = $caBundle;
        }
        return $options;
    }

    private function cacheKey(string $memberId): string
    {
        return 'third_game_total_balance:' . $memberId;
    }

    private function detailCacheKey(string $memberId): string
    {
        return 'third_game_balance_detail:' . $memberId;
    }

    private function clearCache(string $memberId): void
    {
        Cache::forget($this->cacheKey($memberId));
        Cache::forget($this->detailCacheKey($memberId));
    }

    private function random(): string
    {
        $characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $value = '';
        for ($i = 0; $i < 32; $i++) {
            $value .= $characters[random_int(0, strlen($characters) - 1)];
        }
        return $value;
    }
}
