<?php
namespace app\common\service;

use AllowDynamicProperties;
use app\admin\model\Config as ConfigModel;
use app\common\model\UserScoreLog;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\RequestException;
use think\helper\Str;

#[AllowDynamicProperties] class XyxServices
{
    public $method = 'POST';
    private $domain;
    private $appId;
    private $appSecret;
    private $player;
    private $account;
    private $password;

    public $gameType = [
        'ECasino' => 'Casino',
        'Fishing' => 'Fishing',
        'Bingo' => 'Bingo',
        'Slot' => 'Slot',
    ];

    public $localGameType = [
        'slot' => '2',
        'multiplayer' => '1',
        'fishing' => '7',
        'bet' => '5',
    ];
    public $failCode = [
        '0' => '应用不存在',
        '1000' => '参数错误',
        '1101' => '玩家账号',
        '1102' => '密码',
        '1103' => '玩家id',
        '1204' => '金额',
        '1205' => '开始时间',
        '1206' => '结束时间',
        '2001' => '渠道不存在',
        '2100' => '用户错误',
        '2102' => '用户已注册',
        '2103' => '玩家不存在',
        '2104' => '玩家已被停用',
        '2205' => '钱包余额不足',
        '2206' => '结束时间必须大于起始时间',
        '2207' => '查询时间不能超过1小时',
        '3000' => '系统错误',
        '3201' => '钱包转出失败',
        '3202' => '钱包转入失败',
        '3902' => 'JWT清除失败',
    ];

    public function __construct($player = null)
    {
        $config = ConfigModel::where('group', 'mini_games')->column('value', 'name');
        $this->appId = $config['app_id'];
        $this->appSecret = $config['app_secret'];
        $this->domain = $config['domain'];
        if (!empty($player)) {
            $this->player = $player;
            $this->account = $player['xyx_account'];
            $this->password = $player['xyx_password'];
            if (empty($this->account) || empty($this->password)) {
                $this->createPlayer($player);
            }
        }
    }

    /**
     * 生成请求
     * @param $url
     * @param array $params
     * @return array|mixed
     * @throws \Exception
     */
    public function doXyxCurl($url, array $params = [])
    {
        $headers = [
            'appId' => $this->appId,
            'timestamp' => time(),
            'nonceStr' => Str::random(6),
        ];
        $data = array_merge($params,$headers);
        ksort($data);
        $signature = hash('sha256', urldecode(http_build_query($data)) . $this->appSecret);
        $headers['signature'] = $signature;

        $client = new Client([
            'timeout'  => 7,
            'verify'   => true,
        ]);
        try {
            $response = $client->post($url, [
                'json'    => $params, // 相当于 asJson()，会自动设置 Content-Type: application/json
                'headers' => $headers,
            ]);

            if ($response->getStatusCode() !== 200) {
                throw new \Exception(lang('system_busy')); // ThinkPHP 使用 lang() 代替 trans()
            }

            $contents = $response->getBody()->getContents();
            $data = json_decode($contents, true);

            if (empty($data)) {
                throw new \Exception(lang('system_busy'));
            }

            return $data;

        } catch (GuzzleException $e) {
            // 请求失败（如超时、证书错误等）
            throw new \Exception(lang('system_busy'));
        }
    }

    /**
     * 获取token
     */
    public function getToken($gameId)
    {
        $params = [
            'account' => $this->account,
            'password' => $this->password,
            'game_id' => $gameId,
        ];
        $res = $this->doXyxCurl($this->domain . 'agent/api/get-access-token', $params);
        if ($res['code'] != 200) {
            throw new \Exception($this->failCode[$res['code']]);
        }
        return $res['data']['access_token'];
    }

    /**
     * 创建玩家
     */
    public function createPlayer($player)
    {
        $params = [
            'account' => 'jk' . $player['jk_user_id'],
            'password' => Str::random(12),
        ];
        $res = $this->doXyxCurl($this->domain . 'agent/api/create-player', $params);
        if ($res['code'] != 200) {
            throw new \Exception($this->failCode[$res['code']]);
        }
        $player->xyx_account = $params['account'];
        $player->xyx_password = $params['password'];
        $player->save();
        $this->account = $params['account'];
        $this->password = $params['password'];
        return $res;
    }

    /**
     * 玩家进入游戏
     * @param array $data
     * @return string
     */
    public function login(array $data = []): string
    {
        $token = $this->getToken($data['gameCode']);
        $out = UserScoreLog::where('user_id', $this->player->id)
            ->where('type', 2)
            ->whereNotNull('created_by')
            ->order('id', 'desc')
            ->find();
        if ($out) {
            $this->balanceTransferIn($out->game_type);
        }
        $this->balanceTransferOut($data['gameCode']);
        $headers = [
            'appId' => $this->appId,
            'timestamp' => time(),
            'nonceStr' => Str::random(6),
        ];
        ksort($headers);
        $signature = hash('sha256', urldecode(http_build_query($headers)) . $this->appSecret);
        $headers['signature'] = $signature;
        $headers['Authorization'] = 'Bearer ' . $token;

        $client = new Client([
            'timeout'  => 7,
            'allow_redirects'   => false,
        ]);

        try {
            $response = $client->request('GET', $this->domain . 'agent/enter-game', [
                'headers' => $headers,
            ]);
            if ($response->getStatusCode() == 302) {
                return $response->getHeader('Location')[0] ?? '';
            }
        } catch (RequestException $e) {
            if ($e->hasResponse() && $e->getResponse()->getStatusCode() == 302) {
                return $e->getResponse()->getHeader('Location')[0] ?? '';
            }
        }

        return false;
    }

    /**
     * 获取玩家游戏平台余额
     */
    public function getBalance()
    {
        $params = [
            'account' => $this->account,
        ];
        $res = $this->doXyxCurl($this->domain . 'agent/api/get-player-info', $params);
        if ($res['code'] != 200) {
            throw new \Exception($this->failCode[$res['code']]);
        }
        return $res['data']['point'];
    }

    /**
     * 玩家钱包转入游戏平台
     */
    public function balanceTransferOut($gameType)
    {
        $score = $this->player->userScore()->where('game_type', $gameType)->value('score');
        if ($score == 0) {
            return true;
        }
        return $this->setBalanceTransfer(2, $gameType, $score);
    }

    /**
     * 游戏平台转入玩家钱包
     */
    public function balanceTransferIn($gameType)
    {
        $balance = $this->getBalance();
        if ($balance == 0) {
            $userScore = $this->player->userScore()->where('game_type', $gameType)->find();
            $userScoreLog = new UserScoreLog();
            $userScoreLog->save([
                'user_id'   => $this->player->id,
                'game_type' => $gameType,
                'before'    => $userScore->score,
                'after'     => $userScore->score,
                'score'     => 0,
                'memo'      => '转入',
                'type'      => 1,
                'created_by'=> 0,
            ]);
            return true;
        }
        return $this->setBalanceTransfer(1, $gameType, $balance);
    }

    /**
     *  轉帳進出額度
     * @param $type
     * @param float $amount
     * @return array|mixed|null
     * @throws \Exception
     */
    protected function setBalanceTransfer($type,$gameType, float $amount = 0)
    {
        $yCode = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
        $tradeno = $yCode[intval(date('Y')) - 2011] . strtoupper(dechex(date('m'))) . date('d') . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));
        $params = [
            'account' => $this->account,
            'point' => $amount,
            'tradeno' => $tradeno
        ];
        if ($type == 2) {
            $res = $this->doXyxCurl($this->domain . 'agent/api/transfer-in', $params);
        } else {
            $res = $this->doXyxCurl($this->domain . 'agent/api/transfer-out', $params);
        }
        if ($res['code'] != 200) {
            throw new \Exception($this->failCode[$res['code']]);
        }
        $userScore = $this->player->userScore()->where('game_type', $gameType)->find();
        $userScore->created_by = 0;
        if ($type == 2) {
            $userScore->score = 0;
            $userScore->memo = '转出';
        } else {
            $userScore->score += $amount;
            $userScore->memo = '转入';
        }
        $userScore->save();
        return $res;
    }

    public function getReportRecord(string $startTime = '', string $endTime = '')
    {
        $params = [
            'start_date' => $startTime,
            'end_date' => $endTime,
        ];
        $res = $this->doXyxCurl($this->domain . 'agent/api/draw-records-by-date', $params);
        if ($res['code'] != 200) {
            throw new \Exception($this->failCode[$res['code']]);
        }
        return $res['data'];
    }
}
