<?php
/**
 * 用户登录相关
 * User: benzhan
 * Date: 2018/10/18
 * Time: 10:41
 */

class UserController extends BaseController {

    protected $ajaxLoginActions = [
        'eosBind',
        'eosBind2',
        'ethBind',
        'tronBind',
        'tgBind',
        'tgBind2',
        'unbind',
        'changeUserName',
        'changeNickName',
        'changePhoto',
        'setVisible',
    ];

    public function __construct() {
        parent::__construct(false);
    }

    /**
     * 获取随机数【不需要登录】
     * @author benzhan
     */
    public function actionRandom($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^([a-z0-9\.]{1,13})$|^(0x[a-z0-9]{40})$|^[a-zA-Z0-9]{34}$/i', 'desc' => 'eos账号|eth账号|tron账号'],
        ];
        Param::checkParam2($rules, $args);

        $random = Account::getRandom($args['account']);
        Response::success($random);
    }

    /**
     * 获取随机数【不需要登录】
     * @author benzhan
     */
    public function actionRandom2($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^([a-z0-9\.]{1,13})$|^(0x[a-z0-9]{40})$|^[a-zA-Z0-9]{34}$/i', 'desc' => 'eos账号|eth账号'],
        ];
        Param::checkParam2($rules, $args);

        $random = Account::getRandom($args['account'], true);
        Response::success($random);
    }

    private function _getUserBaseInfo($user_id) {
        $objUserInfo = new TableHelper('user_info', 'dw_chat');
        $row = $objUserInfo->getRow(['user_id' => $user_id], ['_field' => 'user_id, user_name, nick_name, cover_photo']);
        return $row;
    }

    /**
     * Eos账号登录【不需要登录】
     * @author benzhan
     */
    public function actionEosLogin($args, $orginStr = null) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-z0-9\.]{1,13}$/', 'desc' => 'eos账号'],
            'pubkey' => ['string', 'nullable' => true, '公钥'],
            'authority' => ['string', 'nullable' => true, 'enum' => ['owner', 'active'], '权限'],
            'sign' => ['string', 'desc' => '私钥加密后的签名']
        ];
        Param::checkParam2($rules, $args);

        $info = User::getInfo();
        if ($info) {
            $info['info'] = $this->_getUserBaseInfo($info['user_id']);
            return $info;
        }

        if (!$args['pubkey'] && $args['authority']) {
            $args['pubkey'] = $this->_getPublicKey($args['account'], $args['authority']);
        }

        if (!$args['pubkey']) {
            Response::error(CODE_PARAM_ERROR, 'pubkey is empty.');
        }

        $account = $args['account'];
        if ($orginStr) {
            $data = $orginStr;
        } else {
            $data = Account::getRandom($account);
            if (!$data) {
                Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
            }
        }

        $flag = false;
        try {
            $flag = Account::verifyMsg($args['pubkey'], $account, $data, $args['sign']);
        } catch (Exception $e) {
            Response::error($e->getCode(), $e->getMessage());
        }

        if ($flag) {
            $user_id = User::login($args['account'], Account::TYPE_EOS);
            $info = Account::setCookie($user_id);
            $info['info'] = $this->_getUserBaseInfo($info['user_id']);
            return $info;
        } else {
            if ($orginStr) {
                return false;
            } else {
                Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
            }
        }
    }

    /**
     * 校验登录 (合约方式
     * @author solu
     * @param $args
     * @return array
     */
    public function actionEosLogin2($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-z0-9\.]{1,13}$/', 'desc' => 'eos账号'],
            'access_token' => ['string', 'len' => 36, 'desc' => 'access_token'],
            'trx_id' => ['string', 'desc' => '交易id'],
            'group_id' => ['int', 'nullable' => true, 'desc' => '群id'],
        ];
        Param::checkParam2($rules, $args);

        $objSync = new Sync_LoginLog();
        $objLoginLog = new TableHelper('login_log', 'dw_chat');
        $_where = ['trx_id' => $args['trx_id']];
        $row = $objLoginLog->getRow($_where);

        $maxTry = 3;
        $i = 0;
        // 没有数据请求同步再尝试
        while ($i < $maxTry && !$row) {
            sleep($i + 1);
            $objSync->pubSubscribe();

            $row = $objLoginLog->getRow($_where);
            $i++;
        }

        if (!$row) {
            Response::error(CODE_NORMAL_ERROR, 'trx_id not in log!');
        }

        $account = $args['account'];
        $random = Account::getRandom($account);
        if (!$random) {
            Response::error(CODE_NORMAL_ERROR, 'can not find random data, please refresh.');
        }

        if ($row['account'] != $account) {
            Response::error(CODE_NORMAL_ERROR, 'account unmatch');
        }

        if ($random != $row['memo']) {
            Response::error(CODE_NORMAL_ERROR, 'random unmatch');
        }

        $data = [];
        try {
            $data = Account::verifyPlayer($row['account'], $random, $args['access_token']);
        } catch (Exception $e) {
            Response::error($e->getCode(), $e->getMessage());
        }

        // 带群id检测是否加入群
        if ($args['group_id']) {
            try {
                (new GroupInfo())->joinGroup($data['user_id'], $args['group_id']);
            } catch (Exception $e) {
                var_log($e->getMessage());
            }

        }

        return $data;
    }

    private function _getPublicKey($account, $authority) {
        $accountInfo = EosBase::getAccount($account);
        $accountInfo = json_decode($accountInfo, true);
        foreach ($accountInfo['permissions'] as $p) {
            if ($p['perm_name'] == $authority) {
                return $p['required_auth']['keys'][0]['key'];
            }
        }

        return null;
    }

    /**
 * Eos账号绑定
 * @author benzhan
 */
    public function actionEosBind($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-z0-9\.]{1,13}$/', 'desc' => 'eos账号'],
            'pubkey' => ['string', '公钥'],
            'sign' => ['string', 'desc' => '私钥加密后的签名'],
        ];
        Param::checkParam2($rules, $args);

        $account = $args['account'];
        $data = Account::getRandom($account);
        if (!$data) {
            Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
        }

        $flag = Account::verifyMsg($args['pubkey'], $account, $data, $args['sign']);
        if ($flag) {
            $user_id = User::getUserId();
            User::bind($user_id, $args['account'], Account::TYPE_EOS);
        } else {
            Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
        }
    }

    /**
     * Eos账号绑定（合约方式
     * @author solu
     * @author benzhan
     */
    public function actionEosBind2($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-z0-9\.]{1,13}$/', 'desc' => 'eos账号'],
            'access_token' => ['string', 'len' => 36, 'desc' => 'access_token'],
            'trx_id' => ['string', 'desc' => '交易id'],
        ];
        Param::checkParam2($rules, $args);

        $objSync = new Sync_LoginLog();
        $objLoginLog = new TableHelper('login_log', 'dw_chat');
        $_where = ['trx_id' => $args['trx_id']];
        $row = $objLoginLog->getRow($_where);

        $maxTry = 3;
        $i = 0;
        // 没有数据请求同步再尝试
        while ($i < $maxTry && !$row) {
            sleep($i + 1);
            $objSync->pubSubscribe();

            $row = $objLoginLog->getRow($_where);
            $i++;
        }

        if (!$row) {
            Response::error(CODE_NORMAL_ERROR, 'trx_id not in log!');
        }

        $account = $args['account'];
        $random = Account::getRandom($account);
        if (!$random) {
            Response::error(CODE_NORMAL_ERROR, 'can not find random data, please refresh.');
        }

        if ($row['account'] != $account) {
            Response::error(CODE_NORMAL_ERROR, 'account unmatch');
        }

        if ($random != $row['memo']) {
            Response::error(CODE_NORMAL_ERROR, 'random unmatch');
        }

        try {
            Account::verifyEosOnly($account, $random, $args['access_token']);
        } catch (Exception $e) {
            Response::error($e->getCode(), $e->getMessage());
        }

        $user_id = User::getUserId();
        User::bind($user_id, $account, Account::TYPE_EOS);
    }

    /**
     *  Eth账号登录【不需要登录】
     * @author solu
     */
    public function actionEthLogin($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^0x[a-z0-9]{40}$/i', 'desc' => 'eos账号'],
            'sign' => ['string', 'desc' => '私钥加密后的签名']
        ];
        Param::checkParam2($rules, $args);

        $info = User::getInfo();
        if ($info) {
            return $info;
        }

        $account = $args['account'];
        $data = Account::getRandom($account);
        if (!$data) {
            Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
        }
        $flag = false;
        try {
            $flag = Account::verifyEth($args['account'], $data, $args['sign']);
        } catch (Exception $e) {}

        if (!$flag) {
            Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
        }

        $user_id = User::login($args['account'], Account::TYPE_ETH);
        return Account::setCookie($user_id);
    }

    /**
     * ETH账号绑定
     * @author solu
     * @param $args
     */
    public function actionEthBind($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^0x[a-z0-9]{40}$/i', 'desc' => 'eos账号'],
            'sign' => ['string', 'desc' => '私钥加密后的签名']
        ];
        Param::checkParam2($rules, $args);

        $account = $args['account'];
        $data = Account::getRandom($account);
        if (!$data) {
            Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
        }
        $flag = false;
        try {
            $flag = Account::verifyEth($args['account'], $data, $args['sign']);
        } catch (Exception $e) {}

        if (!$flag) {
            Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
        }

        $user_id = User::getUserId();
        User::bind($user_id, $account, Account::TYPE_ETH);
    }

    /**
     * Tron账号登录【不需要登录】
     * @param $args
     * @return array
     */
    public function actionTronLogin($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-zA-Z0-9]{34}$/', 'desc' => 'tron账号'],
            'sign' => ['string', 'desc' => '私钥加密后的签名']
        ];
        Param::checkParam2($rules, $args);

        $info = User::getInfo();
        if ($info) {
            return $info;
        }

        $account = $args['account'];
        $data = Account::getRandom($account);
        if (!$data) {
            Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
        }
        $flag = false;
        try {
            $flag = Account::verifyTron($args['account'], $data, $args['sign']);
        } catch (Exception $e) {}

        if (!$flag) {
            Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
        }

        $user_id = User::login($args['account'], Account::TYPE_TRON);
        return Account::setCookie($user_id);
    }

    /**
     * Tron账号绑定
     * @author solu
     * @param $args
     */
    public function actionTronBind($args) {
        $rules = [
            'account' => ['string', 'reg' => '/^[a-zA-Z0-9]{34}$/', 'desc' => 'tron账号'],
            'sign' => ['string', 'desc' => '私钥加密后的签名']
        ];
        Param::checkParam2($rules, $args);

        $account = $args['account'];
        $data = Account::getRandom($account);
        if (!$data) {
            Response::error(CODE_NO_PERMITION, 'can not find random data, please refresh.');
        }
        $flag = false;
        try {
            $flag = Account::verifyTron($args['account'], $data, $args['sign']);
        } catch (Exception $e) {}

        if (!$flag) {
            Response::error(CODE_NORMAL_ERROR, 'Verify failure, please retry.');
        }

        $user_id = User::getUserId();
        User::bind($user_id, $account, Account::TYPE_TRON);
    }

    /**
     * 解绑账号
     * @author solu
     * @param $args
     */
    public function actionUnbind($args) {
        $typeRule = array_keys(Account::getAllType());
        $rules = [
            'type' => ['string', 'enum' => $typeRule, 'desc' => '类型 eos,eth, tron'],
        ];
        Param::checkParam2($rules, $args);

        $userId = User::getUserId();
        try {
            User::unbind($userId, $args['type']);
        } catch (Exception $e) {
            Response::error($e->getCode(), $e->getMessage());
        }
    }

    /**
     * 修改用户名【只能修改一次】
     * @author benzhan
     */
    public function actionChangeUserName($args) {
        $rules = [
            'user_name' => ['string', 'reg' => '/^[a-zA-Z_0-9]{5,20}$/i', 'desc' => '用户名'],
        ];
        Param::checkParam2($rules, $args);

        $user_id = User::getUserId();
        User::saveInfo($user_id, $args);
    }

    /**
     * 修改昵称
     * @author benzhan
     */
    public function actionChangeNickName($args) {
        $rules = [
            'nick_name' => ['string', 'len' => [1, 20], 'desc' => '用户名'],
        ];
        Param::checkParam2($rules, $args);

        $user_id = User::getUserId();
        User::saveInfo($user_id, $args);
        User::setUserNameById($user_id, $args['nick_name']);
    }

    /**
     * 修改头像
     * @author benzhan
     */
    public function actionChangePhoto($args) {
        $args = array_merge($args, $_FILES);
        $rules = [
            'cover_photo' => ['array', 'desc' => '头像文件'],
        ];
        Param::checkParam2($rules, $args);

        $file = $args['cover_photo'];
        $cover_photo = '';
        try {
            $cover_photo = (new FileUrl())->getFileUrl($file['tmp_name'], $file['name'], $file['type'], true);
        } catch (Exception $e) {
            Response::error($e->getCode(), $e->getMessage());
        }

        $user_id = User::getUserId();
        User::saveInfo($user_id, compact('cover_photo'));
    }

    /**
     * 第三方账号的是否可见
     * @author benzhan
     */
    public function actionSetVisible($args) {
        $types = array_keys(Account::getAllType());
        $rules = [
            'type' => ['string', 'enum' => $types, 'desc' => '类型'],
            'is_visible' => ['int', 'desc' => '是否可见'],
        ];
        Param::checkParam2($rules, $args);

        $user_id = User::getUserId();
        $type = $args['type'];
        $is_visible = (int) $args['is_visible'];

        $objUserBindInfo = new TableHelper('user_bind_info', 'dw_chat');
        $objUserBindInfo->updateObject(compact('is_visible'), compact('user_id', 'type'));
    }

    /**
     * 用户信息接口
     * @author solu
     * @param $args
     * @return array
     */
    public function actionInfo($args) {
        $rules = [
            'target_id' => ['int', 'desc' => '用户id'],
            'group_id' => ['int', 'nullable' => true, '群id'],
        ];
        Param::checkParam2($rules, $args);

        $self = User::getUserId();
        $userId = (int)$args['target_id'];
        $groupId = (int)$args['group_id'];

        // 如果是自己调用，则刷新token的有效期
        if ($userId == $self) {
            Account::refreshToken();
        }

        return User::getUserInfo($userId, $self, $groupId);
    }

    /**
     * Simplewallet 协议登录
     * @param $args
     * @param string $authority
     *
     * @return array|bool|null
     */
    public function actionSimplewallet($args, $authority = 'active') {
        if (!$args) {
            $json = file_get_contents('php://input');
            $data = json_decode($json, true);
            $args += $data;
        }

        $rules = [
            'account' => ['string', 'desc' => 'Eos账号'],
            'chainId' => ['string', 'desc' => '链id'],
            'protocol' => ['string', 'desc' => '协议', 'enum' => ['SimpleWallet']],
            'ref' => ['string', 'desc' => '来源'],
            'sign' => ['string', 'desc' => '签名'],
            'timestamp' => ['string', 'desc' => '时间戳'],
            'uuID' => ['string', 'desc' => '随机数'],
            'version' => ['string', 'desc' => '版本号'],
        ];
        Param::checkParam($rules, $args);

        $pubkey = $this->_getPublicKey($args['account'], $authority);
        $newArgs = [
            'account' => $args['account'],
            'pubkey' => $pubkey,
            'authority' => $authority,
            'sign' => $args['sign'],
        ];

        $orginStr = "{$args['timestamp']}{$args['account']}{$args['uuID']}{$args['ref']}";
        $info = $this->actionEosLogin($newArgs, $orginStr);
        if ($info) {
            $objRedis = dwRedis::init();
            $info['account'] = $args['account'];
            $uuid = $this->_getUuid($args['uuID']);
            $objRedis->setex($uuid, 60, json_encode($info));
        } else if ($authority == 'active') {
            // 再尝试一次 owner
            $this->actionSimplewallet($args, 'owner');
        }
    }

    /**
     * Simplewallet 协议登录
     * @param $args
     * @param string $authority
     *
     * @return array|bool|null
     */
    public function actionSimplewalletCheck($args) {
        $rules = [
            'uuID' => ['string', 'desc' => '随机数']
        ];
        Param::checkParam($rules, $args);

        $objRedis = dwRedis::init();
        $uuid = $this->_getUuid($args['uuID']);
        $json = $objRedis->get($uuid);
        if ($json) {
            $info = json_decode($json, true);
            return $info;
        } else {
            Response::error(CODE_NORMAL_ERROR, 'no found');
        }
    }

    private function _getUuid($uuid) {
        return "globals:simple_wallet:{$uuid}";
    }


    /**
     * Telegram登录
     * @author solu
     * @param $args
     * @return array
     * @ignore
     */
    public function actionTgLogin($args) {
        $rules = [
            'id' => ['int', 'desc' => 'telegram user id'],
            'first_name' => 'string',
            'last_name' => ['string', 'nullable' => true],
            'auth_date' => ['int', 'desc' => 'timestamp'],
            'hash' => ['string', 'desc' => 'verify hash'],
        ];
        Param::checkParam2($rules, $args);

        $data = [];
        try {
            $data = ThirdApi::checkTelegramAuthorization($args);
        } catch (Exception $e) {
            Response::error(CODE_SIGN_ERROR, $e->getMessage());
        }

        $name = $data['first_name'];
        $args['last_name'] && $name .= "_{$args['last_name']}";
        $user_id = User::login($data['id'], Account::TYPE_TG, $name);
        return Account::setCookie($user_id);
    }

    /**
     * Telegram绑定
     * @author solu
     * @param $args
     * @ignore
     */
    public function actionTgBind($args) {
        $rules = [
            'id' => ['int', 'desc' => 'telegram user id'],
            'first_name' => 'string',
            'last_name' => ['string', 'nullable' => true],
            'auth_date' => ['int', 'desc' => 'timestamp'],
            'hash' => ['string', 'desc' => 'verify hash'],
        ];
        Param::checkParam2($rules, $args);

        $data = [];
        try {
            $data = ThirdApi::checkTelegramAuthorization($args);
        } catch (Exception $e) {
            Response::error(CODE_SIGN_ERROR, $e->getMessage());
        }

        $user_id = User::getUserId();
        User::bind($user_id, $data['id'], Account::TYPE_TG);
    }

    /**
     * Telegram登录token
     * @author solu
     * @param $args
     * @return array
     */
    public function actionTgCSRF($args) {
        $rules = [
            'type' => ['string', 'enum' => ['login', 'bind'], 'desc' => '类型'],
        ];
        Param::checkParam2($rules, $args);

        $csrf_token = $args['type'] . '-' . uuid();
        Telegram::initCSRF($csrf_token);

        $url = BOT_CHAT_URL . "?start={$csrf_token}";

        return compact('csrf_token', 'url');
    }

    /**
     * Telegram登录（窗口start模式
     * @param $args
     * @return array
     */
    public function actionTgLogin2($args) {
        $rules = [
            'csrf_token' => 'string',
        ];
        Param::checkParam2($rules, $args);

        $status = Telegram::getCSRFStatus($args['csrf_token']);
        $user_id = 0;
        $token = '';
        if ($status > 0) { // 用户已在Telegram确定
            $userData = Account::setCookie($status);
            $user_id = $userData['user_id'];
            $token = $userData['token'];
        }

        return compact('status', 'user_id', 'token');
    }

    /**
     * Telegram绑定 （窗口start模式
     * @param $args
     * @return array
     */
    public function actionTgBind2($args) {
        $rules = [
            'csrf_token' => 'string',
        ];
        Param::checkParam2($rules, $args);

        $user_id = User::getUserId();
        $status = Telegram::getCSRFStatus($args['csrf_token']);
        if ($status > 0) { // 用户已在Telegram确定
            User::bind($user_id, $status, Account::TYPE_TG);
            Telegram::setUserByTG($status, $user_id);
        }

        return compact('status');
    }

    /**
     * 校验登录态
     * @author solu
     * @param $args
     * @return array
     */
    public function actionCheckLogin($args) {
        $rules = [
            'user_id' => 'int',
            'token' => 'string',
        ];
        Param::checkParam2($rules, $args);
        $user_id = User::getUserId();

        $is_login = $user_id > 0;

        return compact('is_login');
    }

}