<?php




namespace app\controller\api;


use app\common\repositories\user\UserRepository;
use app\common\repositories\wechat\RoutineQrcodeRepository;
use app\common\repositories\wechat\WechatUserRepository;
use app\validate\api\ChangePasswordValidate;
use app\validate\api\UserAuthValidate;
use ln\basic\BaseController;
use ln\services\MiniProgramService;
use ln\services\WechatService;
use ln\services\YunxinSmsService;
use Exception;
use Gregwar\Captcha\CaptchaBuilder;
use Gregwar\Captcha\PhraseBuilder;
use Symfony\Component\HttpFoundation\Request;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Cache;


/**
 * Class Auth
 * @package app\controller\api
 * @author xaboy
 * @day 2020-05-06
 */
class Auth extends BaseController
{
    public function test(){

    }

    /**
     * @param UserRepository $repository
     * @return mixed
     * @throws DbException
     * @author xaboy
     * @day 2020/6/1
     */
    public function login(UserRepository $repository)
    {
        $account = $this->request->param('account');
        if(Cache::get('api_login_freeze_'.$account))
            return app('json')->fail('账号或密码错误次数太多，请稍后在尝试');
        if (!$account)
            return app('json')->fail('请输入账号');
        $user = $repository->accountByUser($this->request->param('account'));
        if (!$user) $this->loginFailure($account);
        if (!password_verify($pwd = (string)$this->request->param('password'), $user['pwd'])) $this->loginFailure($account);
        $user = $repository->mainUser($user);
        $pid = $this->request->param('spread', 0);
        $repository->bindSpread($user, intval($pid));

        $tokenInfo = $repository->createToken($user);
        $repository->loginAfter($user);

        return app('json')->success($repository->returnToken($user, $tokenInfo));
    }

    /**
     * TODO 登录尝试次数限制
     * @param $account
     * @param int $number
     * @param int $n
     * @author Qinii
     * @day 7/6/21
     */
    public function loginFailure($account,$number = 5,$n = 3)
    {
        $key = 'api_login_failuree_'.$account;
        $numb = Cache::get($key) ?? 0;
        $numb++;
        if($numb >= $number){
            $fail_key = 'api_login_freeze_'.$account;
            Cache::set($fail_key,1,15*60);
            throw new ValidateException('账号或密码错误次数太多，请稍后在尝试');

        }else{
            Cache::set($key,$numb,5*60);

            $msg = '账号或密码错误';
            $_n = $number - $numb;
            if($_n <= $n){
                $msg .= ',还可尝试'.$_n.'次';
            }
            throw new ValidateException($msg);
        }
    }


    /**
     * @return mixed
     * @author xaboy
     * @day 2020/6/1
     */
    public function userInfo()
    {
        $user = $this->request->userInfo()->hidden(['label_id', 'group_id', 'pwd', 'addres', 'card_id', 'last_time', 'last_ip', 'create_time', 'mark', 'status', 'spread_uid', 'spread_time', 'real_name', 'birthday', 'brokerage_price']);
        $user->append(['service', 'total_collect_product', 'total_collect_store', 'total_coupon', 'total_visit_product', 'total_unread', 'total_recharge', 'lock_integral', 'total_integral']);
        $data = $user->toArray();
        $data['total_consume'] = $user['pay_price'];
        $data['extension_status'] = systemConfig('extension_status');
        return app('json')->success($data);
    }

    /**
     * @param UserRepository $repository
     * @return mixed
     * @author xaboy
     * @day 2020/6/1
     */
    public function logout(UserRepository $repository)
    {
        $repository->clearToken($this->request->token());
        return app('json')->success('退出登录');
    }

    /**
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author xaboy
     * @day 2020-05-11
     */
    public function auth()
    {
        $request = $this->request;
        $oauth = WechatService::create()->getApplication()->oauth;
        $oauth->setRequest(new Request($request->get(), $request->post(), [], [], [], $request->server(), $request->getContent()));
        try {
            $wechatInfo = $oauth->user()->getOriginal();
        } catch (Exception $e) {
            return app('json')->fail('授权失败[001]', ['message' => $e->getMessage()]);
        }
        if (!isset($wechatInfo['nickname'])) {
            return app('json')->fail('授权失败[002]');
        }
        /** @var WechatUserRepository $make */
        $make = app()->make(WechatUserRepository::class);

        $user = $make->syncUser($wechatInfo['openid'], $wechatInfo);
        if (!$user)
            return app('json')->fail('授权失败[003]');
        /** @var UserRepository $make */
        $userRepository = app()->make(UserRepository::class);
        $user[1] = $userRepository->mainUser($user[1]);

        $pid = $this->request->param('spread', 0);
        $userRepository->bindSpread($user[1], intval($pid));

        $tokenInfo = $userRepository->createToken($user[1]);
        $userRepository->loginAfter($user[1]);

        return app('json')->success($userRepository->returnToken($user[1], $tokenInfo));
    }

    /**
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author xaboy
     * @day 2020-05-11
     */
    public function mpAuth()
    {
        list($code, $post_cache_key) = $this->request->params([
            'code',
            'cache_key',
        ], true);
        $session_key = Cache::get('eb_api_code_' . $post_cache_key);
        if (!$code && !$session_key)
            return app('json')->fail('授权失败,参数有误');
        $miniProgramService = MiniProgramService::create();
        if ($code && !$session_key) {
            try {
                $userInfoCong = $miniProgramService->getUserInfo($code);
                $session_key = $userInfoCong['session_key'];
                $cache_key = md5(time() . $code);
                Cache::set('eb_api_code_' . $cache_key, $session_key, 86400);
            } catch (Exception $e) {
                return app('json')->fail('获取session_key失败，请检查您的配置！', ['line' => $e->getLine(), 'message' => $e->getMessage()]);
            }
        }

        $data = $this->request->params([
            ['spread_spid', 0],
            ['spread_code', ''],
            ['iv', ''],
            ['encryptedData', ''],
        ]);

        try {
            //解密获取用户信息
            $userInfo = $miniProgramService->encryptor($session_key, $data['iv'], $data['encryptedData']);
        } catch (Exception $e) {
            if ($e->getCode() == '-41003') return app('json')->fail('获取会话密匙失败');
            throw $e;
        }
        if (!$userInfo) return app('json')->fail('openid获取失败');
        if (!isset($userInfo['openId'])) $userInfo['openId'] = $userInfoCong['openid'] ?? '';
        $userInfo['unionId'] = $userInfoCong['unionid'] ?? $userInfo['unionId'] ?? '';
        if (!$userInfo['openId']) return app('json')->fail('openid获取失败');

        /** @var WechatUserRepository $make */
        $make = app()->make(WechatUserRepository::class);
        $user = $make->syncRoutineUser($userInfo['openId'], $userInfo);
        if (!$user)
            return app('json')->fail('授权失败');
        /** @var UserRepository $make */
        $userRepository = app()->make(UserRepository::class);
        $user[1] = $userRepository->mainUser($user[1]);
        $code = intval($data['spread_code']['id'] ?? $data['spread_code']);
        //获取是否有扫码进小程序
        if ($code && ($info = app()->make(RoutineQrcodeRepository::class)->getRoutineQrcodeFindType($code))) {
            $data['spread_spid'] = $info['third_id'];
        }
        $userRepository->bindSpread($user[1], intval($data['spread_spid']));
        $tokenInfo = $userRepository->createToken($user[1]);
        $userRepository->loginAfter($user[1]);

        return app('json')->success($userRepository->returnToken($user[1], $tokenInfo));
    }

    public function getCaptcha()
    {
        $codeBuilder = new CaptchaBuilder(null, new PhraseBuilder(4));
        $key = uniqid(microtime(true), true);
        Cache::set('api_captche' . $key, $codeBuilder->getPhrase(), 300);
        $captcha = $codeBuilder->build()->inline();
        return app('json')->success(compact('key', 'captcha'));
    }

    protected function checkCaptcha($uni, string $code): bool
    {
        $cacheName = 'api_captche' . $uni;
        if (!Cache::has($cacheName)) return false;
        $key = Cache::get($cacheName);
        $res = strtolower($key) == strtolower($code);
        if ($res) Cache::delete($cacheName);
        return $res;
    }

    public function verify(UserAuthValidate $validate)
    {
        $data = $this->request->params(['phone', 'code', 'key', ['type', 'login']]);
        $validate->sceneVerify()->check($data);

        $sms_num_key = 'api.auth.num.' . $data['phone'];
        $num = Cache::get($sms_num_key) ? Cache::get($sms_num_key) : 0;
        if ($num > 2) {
            if (!$data['code'])
                return app('json')->make(402, '请输入验证码');
            if (!$this->checkCaptcha($data['key'], $data['code']))
                return app('json')->fail('验证码输入有误');
        }
        $sms = (YunxinSmsService::create());
//        if(!env('APP_DEBUG', false)){
        try {
            $sms_code = str_pad(random_int(1, 9999), 4, 0, STR_PAD_LEFT);
            $sms_time = systemConfig('sms_time') ? systemConfig('sms_time') : 30;
            $sms->send($data['phone'], 'VERIFICATION_CODE', ['code' => $sms_code, 'time' => $sms_time]);
        } catch (Exception $e) {
            return app('json')->fail($e->getMessage());
        }
//        }else{
//            $sms_code =  1234;
//            $sms_time = 5;
//        }
        $sms_key = $sms->sendSmsKey($data['phone'], $data['type']);
        Cache::set($sms_key, $sms_code, $sms_time * 60);
        Cache::set($sms_num_key, $num + 1, 300);
        //'短信发送成功'
        return app('json')->success('短信发送成功');
    }


    public function smsLogin(UserAuthValidate $validate, UserRepository $repository)
    {
        $data = $this->request->params(['phone', 'sms_code', 'spread']);
        $validate->sceneSmslogin()->check($data);
        if (!(YunxinSmsService::create())->checkSmsCode($data['phone'], $data['sms_code'], 'login'))
            return app('json')->fail('验证码不正确');
        $user = $repository->accountByUser($data['phone']);
        if (!$user) $user = $repository->registr($data['phone'], null);
        $user = $repository->mainUser($user);
        $repository->bindSpread($user, intval($data['spread']));

        $tokenInfo = $repository->createToken($user);
        $repository->loginAfter($user);

        return app('json')->success($repository->returnToken($user, $tokenInfo));
    }

    public function changePassword(ChangePasswordValidate $validate, UserRepository $repository)
    {
        $data = $this->request->params(['phone', 'sms_code', 'pwd']);
        $validate->check($data);
        $user = $repository->accountByUser($data['phone']);
        if (!$user) return app('json')->fail('用户不存在');
        if (!(YunxinSmsService::create())->checkSmsCode($data['phone'], $data['sms_code'], 'change_pwd'))
            return app('json')->fail('验证码不正确');
        $user->pwd = $repository->encodePassword($data['pwd']);
        $user->save();
        return app('json')->success('修改成功');
    }

    public function spread(UserRepository $userRepository)
    {
        $data = $this->request->params([
            ['spread_spid', 0],
            ['spread_code', null],
        ]);
        if (isset($data['spread_code']['id']) && ($info = app()->make(RoutineQrcodeRepository::class)->getRoutineQrcodeFindType($data['spread_code']['id']))) {
            $data['spread_spid'] = $info['third_id'];
        }
        $userRepository->bindSpread($this->request->userInfo(), intval($data['spread_spid']));
        return app('json')->success();
    }

    /**
     * TODO 注册账号
     * @param UserAuthValidate $validate
     * @param UserRepository $repository
     * @return \think\response\Json
     * @author Qinii
     * @day 5/27/21
     */
    public function register(UserAuthValidate $validate, UserRepository $repository)
    {
        $data = $this->request->params(['phone', 'sms_code', 'spread','pwd']);
        $validate->check($data);
        if (!(YunxinSmsService::create())->checkSmsCode($data['phone'], $data['sms_code'], 'login'))
            return app('json')->fail('验证码不正确');
        $user = $repository->accountByUser($data['phone']);
        if ($user) return app('json')->fail('用户已存在');
        $user = $repository->registr($data['phone'], $data['pwd']);
        $user = $repository->mainUser($user);
        $repository->bindSpread($user, intval($data['spread']));

        $tokenInfo = $repository->createToken($user);
        $repository->loginAfter($user);

        return app('json')->success($repository->returnToken($user, $tokenInfo));
    }


    /**
     * App微信登陆
     * @param Request $request
     * @return mixed
     */
    public function appAuth()
    {
        $data = $this->request->params(['userInfo']);

        $user = app()->make(WechatUserRepository::class)->syncAppUser($data['userInfo']['unionId'], $data['userInfo']);
        if (!$user)
            return app('json')->fail('授权失败');
        /** @var UserRepository $make */
        $userRepository = app()->make(UserRepository::class);
        $user[1] = $userRepository->mainUser($user[1]);
        $tokenInfo = $userRepository->createToken($user[1]);
        $userRepository->loginAfter($user[1]);

        return app('json')->success($userRepository->returnToken($user[1], $tokenInfo));
    }

    public function getMerCertificate($merId)
    {
        $merId = (int)$merId;
        $data = $this->request->params(['key', 'code']);
        if (!$this->checkCaptcha($data['key'], $data['code']))
            return app('json')->fail('验证码输入有误');
        $certificate = merchantConfig($merId, 'mer_certificate') ?: [];
        if (!count($certificate))
            return app('json')->fail('该商户未上传证书');
        return app('json')->success($certificate);
    }

    public function appleAuth()
    {
        $data = $this->request->params(['openId', 'nickname']);

        $user = app()->make(WechatUserRepository::class)->syncAppUser($data['openId'], [
            'nickName' => (string)$data['nickname'] ?: '用户' . strtoupper(substr(md5(time()), 0, 12))
        ], 'apple');
        if (!$user)
            return app('json')->fail('授权失败');
        /** @var UserRepository $make */
        $userRepository = app()->make(UserRepository::class);
        $user[1] = $userRepository->mainUser($user[1]);
        $tokenInfo = $userRepository->createToken($user[1]);
        $userRepository->loginAfter($user[1]);
        return app('json')->success($userRepository->returnToken($user[1], $tokenInfo));
    }
}
