<?php

namespace app\service\game;

use addons\webman\model\Game;
use addons\webman\model\GamePlatform;
use addons\webman\model\Player;
use addons\webman\model\PlayerGamePlatform;
use addons\webman\model\PlayerWalletTransfer;
use app\exception\GameException;
use Exception;
use support\Response;

class JDBServiceInterface extends GameServiceFactory implements GameServiceInterface
{
    public $method = 'POST';
    public $successCode = '0000';
    public $loginId;
    public $gameType = [
        '10' => 'Slot',
        '12' => 'Casino',
        '13' => 'Arcade',
        '16' => 'Fishing'
    ];
    public $failCode = [
        '0000' => '成功',
        '9999' => '失敗',
        '9001' => '未授權訪問',
        '9002' => '域名為空或域名長度小於 2',
        '9003' => '域名驗證失敗。',
        '9004' => '加密數據為空或加密數據的長度等於 0。',
        '9005' => '斷言（SAML）未通過時間戳驗證。',
        '9006' => '從加密數據中提取 SAML 參數失敗。',
        '9007' => '未知操作。',
        '9008' => '與之前的值相同。',
        '9009' => '超時。',
        '9010' => '讀取超時。',
        '9011' => '重複交易。',
        '9012' => '請稍後再試。',
        '9013' => '系統正在維護。',
        '9014' => '檢測到多帳戶登錄。',
        '9015' => '數據不存在。',
        '9016' => '無效令牌。',
        '9019' => '請求速率限制超過。',
        '9020' => '每次登錄只能獲得一次遊戲票。',
        '9021' => '違反一次性會話策略。',
        '9022' => '遊戲正在維護。',
        '9023' => '不支持的貨幣。',
        '9024' => '贏取倍數必須大於或等於 10 倍。',
        '9025' => '不支持重放遊戲。',
        '9026' => '获胜金额应大于0。',
        '9027' => '不支持演示。',
        '8000' => '輸入參數錯誤，請檢查您的參數是否正確。',
        '8001' => '參數不能為空。',
        '8002' => '參數必須是正整數。',
        '8003' => '參數不能為負數。',
        '8005' => '日期秒格式錯誤',
        '8006' => '時間不符合。',
        '8007' => '參數只能使用數字。',
        '8008' => '找不到參數。',
        '8009' => '時間間隔超過允許範圍。',
        '8010' => '參數長度太長。',
        '8013' => '日期分鐘格式參數錯誤。',
        '8014' => '參數不得超過指定的小數位。',
        '7001' => '找不到指定的父 ID。',
        '7002' => '父級已暫停。',
        '7003' => '父級已鎖定。',
        '7004' => '父級已關閉。',
        '7405' => '您已登出！',
        '7501' => '找不到用戶 ID。',
        '7502' => '用戶已暫停。',
        '7503' => '用戶已鎖定。',
        '7504' => '用戶已關閉。',
        '7505' => '用戶未在玩遊戲。',
        '7506' => '演示帳戶已滿。',
        '7601' => '無效的用戶 ID。請僅使用 a-z、0-9 之間的字符。',
        '7602' => '帳戶已存在。請選擇其他用戶 ID。',
        '7603' => '無效的用戶名。',
        '7604' => '密碼必須至少 6 個字符，包含 1 個字母和 1 個數字。',
        '7605' => '無效的操作代碼。請僅使用數字 2、3、4、5。',
        '6001' => '您的現金餘額不足以取款。',
        '6002' => '用戶餘額為零。',
        '6003' => '取款金額為負。',
        '6004' => '重複轉帳。',
        '6005' => '重複的序列號。',
        '6009' => '存款金額超過上限。',
        '6010' => '餘額超過上限。',
        '6011' => '分配的信用額超過上限。',
        '6012' => '序列號正在進行中。',
        '6901' => '用戶正在玩遊戲，不允許轉移餘額。'
    ];
    private $apiDomain;
    private $domain;
    private $iv;
    private $key;
    private $dc;
    private $parent;
    private $lang = [
        'zh-CN' => 'cn',
        'zh-TW' => 'cn',
        'jp' => 'jpn',
        'en' => 'en',
        'th' => 'th',
        'vi' => 'vi',
        'kr_ko' => 'ko',
        'id' => 'id',
    ];

    public $localGameType = [
        '0' => '1',//斯洛
        '32' => '1',//斯洛
        '50' => '1',//斯洛
        '55' => '1',//斯洛
        '57' => '1',//斯洛
        '58' => '1',//斯洛
        '66' => '1',//斯洛
        '80' => '1',//斯洛
        '90' => '1',//斯洛
        '130' => '1',//斯洛
        '7' => '4',//捕鱼
        '31' => '4',//捕鱼
        '70' => '4',//捕鱼
        '59' => '4',//捕鱼
        '67' => '4',//捕鱼
        '91' => '4',//捕鱼
        '8' => '8',//宾果
        '12' => '8',//宾果
        '60' => '8',//宾果
        '9' => '8',//宾果
        '22' => '8',//宾果
        '30' => '8',//宾果
        '56' => '8',//宾果
        '75' => '8',//宾果
        '81' => '8',//宾果
        '92' => '8',//宾果
        '131' => '8',//宾果
        '120' => '8',//宾果
        '18' => '2',//赌场
        '93' => '2',//赌场
        '132' => '2',//赌场
        '41' => '5',//真人视讯
        '101' => '5',//真人视讯
    ];

    /**
     * @param Player|null $player
     * @param $type
     * @throws Exception
     */
    public function __construct($type, Player $player = null)
    {
        $config = config('game_platform.' . $type);
        $this->iv = $config['iv'];
        $this->apiDomain = $config['api_domain'].'/apiRequest.do';
        $this->domain = $config['domain'];
        $this->key = $config['key'];
        $this->dc = $config['dc'];
        $this->parent = $config['admin_user'];
        $this->platform = GamePlatform::query()->where('name', $type)->first();
        if (!empty($player)) {
            $this->player = $player;
            $this->getLoginId();
        }
    }

    /**
     * 生成请求数据
     * @param $source
     * @return string
     */
    public function padString($source): string
    {
        $paddingChar = ' ';
        $size = 16;
        $x = strlen($source) % $size;
        $padLength = $size - $x;
        $source .= str_repeat($paddingChar, $padLength);
        return $source;
    }

    /**
     * 生成请求数据
     * @param $params
     * @return array
     */
    public function buildParams($params): array
    {
        $data = $this->padString(json_encode($params));
        $encryptData = openssl_encrypt($data, 'AES-128-CBC', $this->key, OPENSSL_NO_PADDING, $this->iv);
        $reqBase64 = base64_encode($encryptData);
        return [
            'dc' => $this->dc,
            'x' => str_replace(array('+','/','=') , array('-','_','') , $reqBase64)
        ];
    }

    /**
     * @throws Exception
     */
    protected function getLoginId()
    {
        /** @var PlayerGamePlatform $playerGamePlatform */
        $playerGamePlatform = PlayerGamePlatform::query()
            ->where('platform_id', $this->platform->id)
            ->where('player_id',$this->player->id)
            ->first();
        if (!empty($playerGamePlatform)) {
            return $this->loginId = $playerGamePlatform->player_code;
        }
        return $this->createPlayer([
            'uuid' => $this->player->uuid,
            'name' => $this->player->name,
        ]);
    }

    /**
     * 创建玩家
     * @param array $data
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function createPlayer(array $data = [])
    {
        $params = [
            'action' => 12,
            'ts' => round(microtime(true) * 1000),
            'lang' => 'cn',
            'parent' => $this->parent,
            'uid' => $this->player->uuid,
            'name' => $this->player->name ?: $this->player->uuid
        ];
        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }
        $playerGamePlatform = new PlayerGamePlatform();
        $playerGamePlatform->player_id = $this->player->id;
        $playerGamePlatform->platform_id = $this->platform->id;
        $playerGamePlatform->player_name = $data['name'];
        $playerGamePlatform->player_code = $data['uuid'];
        $playerGamePlatform->save();
        $this->loginId = $playerGamePlatform->player_code;
        return $res;
    }

    public function getPlayer()
    {
        // TODO: Implement getPlayer() method.
    }

    /**
     * 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function getSimpleGameList()
    {
        $params = [
            'action' => 49,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'lang' => 'en',
        ];
        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }
        $insertData = [];
        if (!empty($res['data'])) {
            foreach ($res['data'] as $data) {
                foreach($data['list'] as $item){
                    $insertData[] = [
                        'platform_id' => $this->platform->id,
                        'game_code' => $item['mType'],
                        'platform_game_type' => $data['gType'],
                        'game_type' => $this->localGameType[$data['gType']],
                        'name' => $item['name'],
                        'game_image' => $item['image'],
                    ];
                }
            }
        }
        if (!empty($insertData)) {
            Game::query()->upsert($insertData, ['platform_id', 'game_code']);
        }
        return $insertData;
    }

    /**
     * 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
     * @param array $data
     * @return string
     * @throws GameException|\think\Exception
     */
    public function login(array $data = []): string
    {
        $params = [
            'action' => 11,
            'ts' => round(microtime(true) * 1000),
            'lang' => $this->lang[$data['lang']] ?? 'en',
            'uid' => $this->loginId,
            'gType' => $data['platformGameType'],
            'mType' => $data['gameCode'],
            'windowMode' => 2,
            'isAPP' => true,
        ];
//        if($data['platformGameType'] || $data['gameCode']){
//            $params['gType'] = $data['platformGameType'];
//            $params['mType'] = $data['gameCode'];
//            $params['windowMode'] = 2;
//            $params['isAPP'] = true;
//        }
        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }
        return $res['path'] ?? '';
    }

    /**
     * 玩家钱包转入游戏平台
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function balanceTransferOut()
    {
        return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_OUT, $this->player->wallet->money);
    }

    /**
     * 转入游戏平台玩家钱包
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function balanceTransferIn()
    {
        //提现之前把用户踢下线
        if($this->checkPlay()){
            $this->userLogout();
            sleep(1);
        }
        $balance = $this->getBalance();
        if($balance == 0){
            // 记录玩家钱包转出转入记录
            $this->createWalletTransfer(PlayerWalletTransfer::TYPE_IN, 0, 0);
            return true;
        }
        return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, $balance ? -$balance : 0);
    }

    /**
     *  轉帳進出額度
     * @param $type
     * @param float $amount
     * @param float $reward
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    protected function setBalanceTransfer($type, float $amount = 0, float $reward = 0)
    {
        $params = [
            'action' => 19,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'uid' => $this->loginId,
            'serialNo' => createOrderNo(),
            'amount' => $amount ?? 0,
        ];
        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }
        // 记录玩家钱包转出转入记录
        $this->createWalletTransfer($type, $amount, $reward);
        return $res;
    }

    /**
     * 獲取玩家餘額信息
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function getBalance()
    {
        $params = [
            'action' => 15,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'uid' => $this->loginId,
        ];

        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException('JDB System Error,Please contact the administrator', 0);
        }

        return $res['data'][0]['balance'] ?? 0;
    }

    /**
     * 查询玩家状态
     * @throws GameException|\think\Exception
     */
    public function checkPlay(): bool
    {
        $params = [
            'action' => 52,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'uid' => $this->loginId,
        ];

        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        //游戏中
        if($res['status'] == $this->successCode){
            return  true;
        }
        //不在游戏中
        if($res['status'] == 7505){
            return  false;
        }
        throw new GameException($this->failCode[$res['status']], 0);
    }

    /**
     * 玩家踢下线
     * @throws GameException|\think\Exception
     */
    public function userLogout(): bool
    {
        $params = [
            'action' => 17,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'uid' => $this->loginId,
        ];

        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }

        return true;
    }

    /**
     * 取得區間內遊戲紀錄
     * @return array
     * @throws Exception
     */
    public function handleOrderHistories(): array
    {
        $list = [];
        try {
            $data = $this->getGameHistories();
            if (!empty($data)) {
                foreach ($data as $item) {
                    if ($item['gType'] == 9 && !empty($item['hasGamble'])) {
                        $item['bet'] = $item['gambleBet'];
                    }
                    $list[] = [
                        'uuid' => $item['playerId'],
                        'platform_id' => $this->platform->id,
                        'game_code' => $item['mtype'],
                        'bet' => abs($item['bet']),
                        'win' => max($item['win'], 0),
                        'order_no' => $item['historyId'],
                        'game_type' => $item['gType'],
                        'original_data' => json_encode($item,JSON_UNESCAPED_UNICODE),
                        'platform_action_at' => date('Y-m-d H:i:s', strtotime($item['lastModifyTime'])),
                        'created_at' => date('Y-m-d H:i:s'),
                        'updated_at' => date('Y-m-d H:i:s'),
                    ];
                }
            }
        } catch (Exception $e) {
            return [];
        }

        return $list;
    }

    /**
     * 取得區間內遊戲紀錄
     * @return array
     * @throws GameException|\think\Exception
     */
    public function getGameHistories(): array
    {
        $params = [
            'action' => 29,
            'ts' => round(microtime(true) * 1000),
            'parent' => $this->parent,
            'starttime' => date('d-m-Y H:i:00', strtotime('-5 minutes')),
            'endtime' => date('d-m-Y H:i:00', strtotime('-4 minutes')),
        ];
        $request = $this->buildParams($params);
        $res = doFormCurl($this->apiDomain, $request);
        if ($res['status'] != $this->successCode) {
            throw new GameException($this->failCode[$res['status']], 0);
        }
        return $res['data'] ?? [];
    }
}