<?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\Log;
use support\Response;

class MonkeyKingServiceInterface extends GameServiceFactory implements GameServiceInterface
{
    public $method = 'POST';
    public $successCode = 'S100';
    public $loginId;
    public $gameType = [
        '10' => 'Slot',
        '12' => 'Casino',
        '13' => 'Arcade',
        '16' => 'Fishing'
    ];
    public $failCode = [
        'S200' => '重復請求',
        '150001' => '無效的簽名',
        '150002' => '無效的SN',
        '150003' => '無效的參數',
        '150004' => '無效的貨幣',
        '150005' => '玩家已存在',
        '150006' => '玩家不存在',
        '150007' => '次级代理不存在',
        '150008' => '執行失敗',
        '150009' => '無效的方法',
        '150010' => '無效的用戶狀態',
        '150011' => '玩家狀態無需更新',
        '150012' => '超出數據範圍',
        '150013' => '無匹配數據',
        '150014' => '登入位置被禁止',
        '150015' => '分數不足夠',
        '150016' => '不支持禮碼',
        '150017' => '交易流水號不得重複',
        '150018' => '系統繁忙',
        '150019' => '日期時間各式錯誤',
        '150020' => '超出時間限制範圍（開始時間與結束時間之間不能大於120分鐘）',
    ];
    private $apiDomain;
    private $domain;
    private $appId;
    private $appSecret;
    private $path = [
        'createPlayer' => '/UserInfo/CreatePlayer',
        'getPlayer' => '/UserInfo/GetPlayer',
        'getGameList' => '/Game/GetGameList',
        'getSimpleGameList' => '/Game/GetSimpleGameList',
        'getLoginH5' => '/UserInfo/GetLoginH5',
        'setPlayerStatus' => '/UserInfo/SetPlayerStatus',
        'getBalance' => '/Account/GetBalance',
        'setBalanceTransfer' => '/Account/SetBalanceTransfer',
        'getGameRecordByTime' => '/Game/GetGameRecordByTime',
        'getGameRecord' => '/Game/GetGameRecord',
        'removeRecords' => '/Game/RemoveRecords',
    ];
    private $lang = [
        'zh-CN' => 'zh-ch',
        'en' => 'en_us',
        'zh_tc' => 'zh_tc',
        'en-us' => 'en-us',
        'id' => 'id',
        'th' => 'th',
        'my' => 'my',
        'vi' => 'vi',
        'fi_fi' => 'fi_fi',
        'kr_ko' => 'kr_ko',
        'hi_hi' => 'hi_hi',
        'br_po' => 'br_po',
        'lo_la' => 'lo_la',
        'cam_dia' => 'en_us', // 柬埔寨语
    ];

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

    /**
     * @throws Exception
     */
    protected function getLoginId()
    {
        /** @var PlayerGamePlatform $playerGamePlatform */
        $playerGamePlatform = $this->player->playerGamePlatform->where('platform_id', $this->platform->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 = [
            'ID' => createOrderNo(),
            'Method' => 'CreatePlayer',
            'SN' => $this->appId,
            'PlayerCode' => $data['uuid'],
        ];
        $params['Signature'] = $this->createSign($params);
        $params['PlayerName'] = $data['uuid'];
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('createPlayer'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            throw new GameException($this->failCode[$res['code']], 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 createSign($params): string
    {
        return md5(implode('', $params) . $this->appSecret);
    }

    /**
     * 生成请求url
     * @param $method
     * @return string
     */
    public function createUrl($method): string
    {
        return $this->apiDomain . $this->path[$method];
    }

    /**
     * 创建玩家
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function getPlayer()
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetPlayer',
            'SN' => $this->appId,
            'LoginId' => $this->loginId,
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $data = doCurl($this->createUrl('getPlayer'), $params);
        Log::info('MonkeyKing请求返回数据', [$data]);
        if ($data['code'] != $this->successCode) {
            throw new GameException($this->failCode[$data['code']], 0);
        }

        return $data;
    }

    /**
     * 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function getSimpleGameList()
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetSimpleGameList',
            'SN' => $this->appId,
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $data = doCurl($this->createUrl('getSimpleGameList'), $params);
        Log::info('MonkeyKing请求返回数据', [$data]);
        if ($data['code'] != $this->successCode) {
            throw new GameException($this->failCode[$data['code']], 0);
        }
        if (!empty($data['data']['games'])) {
            foreach ($data['data']['games'] as $game) {
                Game::query()->updateOrCreate(
                    [
                        'platform_id' => $this->platform->id,
                        'game_code' => $game['gameCode'],
                        'platform_game_type' => $game['type'],
                    ]
                );
            }
        }
        return $data;
    }

    /**
     * 获取游戏摘要MD5 (id+method+sn+APlSecretKey)
     * @param array $data
     * @return string
     * @throws GameException|\think\Exception
     */
    public function login(array $data = []): string
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetLoginH5',
            'SN' => $this->appId,
            'LoginId' => $this->loginId,
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('getLoginH5'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            throw new GameException($this->failCode[$res['code']], 0);
        }
        $responseH5 = [
            'Language' => $this->lang[$data['lang']] ?? 'ch',
            "GameId" => $data['gameCode'],
            'CallbackAddress' => $data['callBackUrl'] ?? '',
            "AppType" => $data['appType'] ?? 1,
            "DeviceType" => 1,
        ];
        $jsonString = json_encode($responseH5);
        $parametersValue = base64_encode($jsonString);

        return $this->domain . '/linkgame' . ($res['data']['loginUrl'] ?? '') . '&' . $parametersValue . '&' . $data['gameCode'];
    }

    /**
     * 設置登入玩家狀態。當玩家處於禁用狀態，該玩家無法在平台進行任何操作，如果玩家在遊戲進行中該玩家將會自動退出遊戲。
     * 簽名密鑰方式    Md5(Id+Method+SN+LoginId+APISecretKey)
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function setPlayerStatus($status)
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetSimpleGameList',
            'SN' => $this->appId,
            'LoginId' => $this->loginId,
        ];
        $params['Signature'] = $this->createSign($params);
        $params['Status'] = $status;
        Log::info('MonkeyKing请求参数', [$params]);
        $data = doCurl($this->createUrl('setPlayerStatus'), $params);
        Log::info('MonkeyKing请求返回数据', [$data]);
        if ($data['code'] != $this->successCode) {
            throw new GameException($this->failCode[$data['code']], 0);
        }

        return $data;
    }

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

    /**
     *  轉帳進出額度
     *  簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
     * @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)
    {
        if ($amount == 0 && $reward == 0) {
            throw new GameException('转出/入金额错误', 0);
        }
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'SetBalanceTransfer',
            'SN' => $this->appId,
            'LoginId' => $this->loginId,
        ];
        $params['Signature'] = $this->createSign($params);
        $params['Amount'] = $amount;
        $params['Reward'] = $reward;
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('setBalanceTransfer'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            Log::error($this->failCode[$res['code']], ['res' => $res]);
            throw new GameException($this->failCode[$res['code']], 0);
        }
        // 记录玩家钱包转出转入记录
        $this->createWalletTransfer($type, $amount, $reward, $res['data']['refId'] ?? '');

        return $res;
    }

    /**
     * 玩家钱包转入游戏平台
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function balanceTransferIn()
    {
        $balance = $this->getBalance();
        return $this->setBalanceTransfer(PlayerWalletTransfer::TYPE_IN, !empty($balance['data']['result']) ? -$balance['data']['result'] : 0, !empty($balance['data']['reward']) ? -$balance['data']['reward'] : 0);
    }

    /**
     * 獲取玩家餘額信息
     * 簽名密鑰方式 MD5 (Id+Method+SN+LoginId+APISecretKey)
     * @return array|mixed|Response
     * @throws GameException|\think\Exception
     */
    public function getBalance()
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetBalance',
            'SN' => $this->appId,
            'LoginId' => $this->loginId,
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $data = doCurl($this->createUrl('getBalance'), $params);
        Log::info('MonkeyKing请求返回数据', [$data]);
        if ($data['code'] != $this->successCode) {
            throw new GameException($this->failCode[$data['code']], 0);
        }

        return $data;
    }

    /**
     * 依據時間獲取遊戲紀錄
     * MD5 (Id+Method+SN+StartTime+EndTime+APISecretKey)
     * @param int $pageIndex
     * @param string $startTime
     * @param string $endTime
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function getGameRecordByTime(int $pageIndex = 1, string $startTime = '', string $endTime = '')
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetGameRecordByTime',
            'SN' => $this->appId,
            'StartTime' => $startTime,
            'EndTime' => $endTime,
        ];
        $pageSize = 500;
        $params['Signature'] = $this->createSign($params);
        $params['PageSize'] = $pageSize;
        $params['PageIndex'] = $pageIndex;
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('getGameRecordByTime'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            throw new GameException($this->failCode[$res['code']], 0);
        }

        return $res;
    }

    /**
     * 獲取玩家遊戲歷史紀錄
     * MD5 (Id+Method+SN+APISecretKey)
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function getGameRecord()
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'GetGameRecord',
            'SN' => $this->appId,
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('getGameRecord'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            throw new GameException($this->failCode[$res['code']], 0);
        }

        return $res;
    }

    /**
     * 刪除遊戲紀錄
     * MD5 (Id+Method+SN+IdsToBeRemoved+APISecretKey)
     * @return array|mixed|null
     * @throws GameException|\think\Exception
     */
    public function removeRecords($idsToBeRemoved)
    {
        $params = [
            'ID' => createOrderNo(),
            'Method' => 'RemoveRecords',
            'SN' => $this->appId,
            'IdsToBeRemoved' => implode(',', $idsToBeRemoved),
        ];
        $params['Signature'] = $this->createSign($params);
        Log::info('MonkeyKing请求参数', [$params]);
        $res = doCurl($this->createUrl('removeRecords'), $params);
        Log::info('MonkeyKing请求返回数据', [$res]);
        if ($res['code'] != $this->successCode) {
            throw new GameException($this->failCode[$res['code']], 0);
        }

        return $res;
    }
}