<?php




namespace app\common\repositories\system\merchant;


use app\common\dao\BaseDao;
use app\common\dao\system\merchant\MerchantAdminDao;
use app\common\model\system\merchant\Merchant;
use app\common\model\system\merchant\MerchantAdmin;
use app\common\repositories\BaseRepository;
use app\common\repositories\system\auth\RoleRepository;
use ln\exceptions\AuthException;
use ln\services\JwtTokenService;
use FormBuilder\Exception\FormBuilderException;
use FormBuilder\Factory\Elm;
use FormBuilder\Form;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Route;
use think\Model;

/**
 * Class MerchantRepository
 * @package app\common\repositories\system\merchant
 * @mixin MerchantAdminDao
 * @author xaboy
 * @day 2020-04-16
 */
class MerchantAdminRepository extends BaseRepository
{

    const PASSWORD_TYPE_ADMIN = 1;
    const PASSWORD_TYPE_MERCHANT = 2;
    const PASSWORD_TYPE_SELF = 3;

    /**
     * MerchantAdminRepository constructor.
     * @param MerchantAdminDao $dao
     */
    public function __construct(MerchantAdminDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * @param $merId
     * @param array $where
     * @param $page
     * @param $limit
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author xaboy
     * @day 2020-04-18
     */
    public function getList($merId, array $where, $page, $limit)
    {
        $query = $this->dao->search($merId, $where, 1);
        $count = $query->count();
        $list = $query->page($page, $limit)->hidden(['pwd', 'is_del'])->select();
        $topAccount = $this->dao->merIdByAccount($merId);
        foreach ($list as $k => $role) {
            if ($topAccount)
                $list[$k]['account'] = $topAccount . '@' . $role['account'];
            $list[$k]['rule_name'] = $role->roleNames();
        }
        return compact('list', 'count');
    }

    /**
     * @param int $merId
     * @param int|null $id
     * @param array $formData
     * @return Form
     * @throws FormBuilderException
     * @author xaboy
     * @day 2020-04-18
     */
    public function form(int $merId, ?int $id = null, array $formData = []): Form
    {
        $form = Elm::createForm(is_null($id) ? Route::buildUrl('merchantAdminCreate')->build() : Route::buildUrl('merchantAdminUpdate', ['id' => $id])->build());

        $rules = [
            Elm::select('roles', '身份', [])->options(function () use ($merId) {
                $data = app()->make(RoleRepository::class)->getAllOptions($merId);
                $options = [];

                foreach ($data as $value => $label) {
                    $options[] = compact('value', 'label');
                }
                return $options;
            })->multiple(true),
            Elm::input('real_name', '管理员姓名'),
            Elm::input('account', '账号')->required(),
            Elm::input('phone', ' 联系电话'),
        ];
        if (!$id) {
            $rules[] = Elm::password('pwd', '密码')->required();
            $rules[] = Elm::password('againPassword', '确认密码')->required();
        }
        $rules[] = Elm::switches('status', '是否开启', 1)->inactiveValue(0)->activeValue(1)->inactiveText('关闭')->activeText('开启');
        $form->setRule($rules);
        return $form->setTitle(is_null($id) ? '添加管理员' : '编辑管理员')->formData($formData);
    }

    /**
     * @param int $merId
     * @param int $id
     * @return Form
     * @throws DataNotFoundException
     * @throws DbException
     * @throws FormBuilderException
     * @throws ModelNotFoundException
     * @author xaboy
     * @day 2020-04-18
     */
    public function updateForm(int $merId, int $id)
    {
        return $this->form($merId, $id, $this->dao->get($id)->toArray());
    }

    /**
     * @param Merchant $merchant
     * @param $account
     * @param $pwd
     * @return BaseDao|Model
     * @author xaboy
     * @day 2020-04-17
     */
    public function createMerchantAccount(Merchant $merchant, $account, $pwd)
    {
        $pwd = $this->passwordEncode($pwd);
        $data = compact('pwd', 'account') + [
                'mer_id' => $merchant->mer_id,
                'real_name' => $merchant->real_name,
                'phone' => $merchant->mer_phone,
                'level' => 0
            ];
        return $this->create($data);
    }


    /**
     * @param $password
     * @return bool|string
     * @author xaboy
     * @day 2020-04-17
     */
    public function passwordEncode($password)
    {
        return password_hash($password, PASSWORD_BCRYPT);
    }

    /**
     * @param string $account
     * @param string $password
     * @return array|Model|null
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @author xaboy
     * @day 2020-04-17
     */
    public function login(string $account, string $password)
    {
        $accountInfo = explode('@', $account, 2);
        if (count($accountInfo) === 1) {
            $adminInfo = $this->dao->accountByTopAdmin($accountInfo[0]);
        } else {
            $merId = $this->dao->accountByMerchantId($accountInfo[0]);
            if (!$merId)
                throw new ValidateException('账号不存在');
            $adminInfo = $this->dao->accountByAdmin($accountInfo[1], $merId);
        }
        if (!$adminInfo)
            throw new ValidateException('账号不存在');
        if ($adminInfo['status'] != 1)
            throw new ValidateException('账号已关闭');
        if (!password_verify($password, $adminInfo->pwd)) $this->loginFailure($account);

        /**
         * @var MerchantRepository $merchantRepository
         */
        $merchantRepository = app()->make(MerchantRepository::class);
        $merchant = $merchantRepository->get($adminInfo->mer_id);
        if (!$merchant)
            throw new ValidateException('商户不存在');
        if (!$merchant['status'])
            throw new ValidateException('商户已被锁定');

        $adminInfo->last_time = date('Y-m-d H:i:s');
        $adminInfo->last_ip = app('request')->ip();
        $adminInfo->login_count++;
        $adminInfo->save();

        return $adminInfo;
    }

    /**
     * TODO 登录尝试次数限制
     * @param $account
     * @param int $number
     * @param int $n
     * @author Qinii
     * @day 7/6/21
     */
    public function loginFailure($account,$number = 5,$n = 3)
    {
        $key = 'mer_login_failuree_'.$account;
        $numb = Cache::get($key) ?? 0;
        $numb++;
        if($numb >= $number){
            $fail_key = 'mer_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);
        }
    }

    /**
     * @param string $token
     * @param int $exp
     * @author xaboy
     * @day 2020-04-10
     */
    public function cacheToken(string $token, int $exp)
    {
        Cache::set('mer_' . $token, time() + $exp, $exp);
    }

    /**
     * @param string $token
     * @author xaboy
     * @day 2020-04-17
     */
    public function checkToken(string $token)
    {
        $has = Cache::has('mer_' . $token);
        if (!$has)
            throw new AuthException('无效的token');
        $lastTime = Cache::get('mer_' . $token);
        if (($lastTime + (intval(Config::get('admin.token_valid_exp', 15))) * 60) < time())
            throw new AuthException('token 已过期');
    }

    /**
     * @param string $token
     * @author xaboy
     * @day 2020-04-17
     */
    public function updateToken(string $token)
    {
        Cache::set('mer_' . $token, time(), intval(Config::get('admin.token_valid_exp', 15)) * 60);
    }

    /**
     * @param string $token
     * @author xaboy
     * @day 2020-04-17
     */
    public function clearToken(string $token)
    {
        Cache::delete('mer_' . $token);
    }

    /**
     * @param MerchantAdmin $admin
     * @return array
     * @author xaboy
     * @day 2020-04-17
     */
    public function createToken(MerchantAdmin $admin)
    {
        $service = new JwtTokenService();
        $exp = intval(Config::get('admin.token_exp', 3));
        $token = $service->createToken($admin->merchant_admin_id, 'mer', strtotime("+ {$exp}hour"));
        $this->cacheToken($token['token'], $token['out']);
        return $token;
    }

    /**
     * @param string $key
     * @param string $code
     * @author xaboy
     * @day 2020-04-17
     */
    public function checkCode(string $key, string $code)
    {
        $_code = Cache::get('am_captcha' . $key);
        if (!$_code) {
            throw new ValidateException('验证码过期');
        }

        if (strtolower($_code) != strtolower($code)) {
            throw new ValidateException('验证码错误');
        }

        //删除code
        Cache::delete('am_captcha' . $key);
    }


    /**
     * @param string $code
     * @return string
     * @author xaboy
     * @day 2020-04-17
     */
    public function createLoginKey(string $code)
    {
        $key = uniqid(microtime(true), true);
        Cache::set('am_captcha' . $key, $code, Config::get('admin.captcha_exp', 5) * 60);
        return $key;
    }

    /**
     * @param int $id
     * @param int $userType
     * @return Form
     * @throws FormBuilderException
     * @author xaboy
     * @day 2020-04-20
     */
    public function passwordForm(int $id, $userType = 2)
    {
        $action = 'merchantAdminPassword';
        if ($userType == self::PASSWORD_TYPE_ADMIN)
            $action = 'systemMerchantAdminPassword';
        else if ($userType == self::PASSWORD_TYPE_SELF)
            $action = 'merchantAdminEditPassword';

        $form = Elm::createForm(Route::buildUrl($action, $userType == self::PASSWORD_TYPE_SELF ? [] : compact('id'))->build(), [
            $rules[] = Elm::password('pwd', '密码')->required(),
            $rules[] = Elm::password('againPassword', '确认密码')->required(),
        ]);
        return $form->setTitle('修改密码');
    }

    /**
     * @param array $formData
     * @return Form
     * @throws FormBuilderException
     * @author xaboy
     * @day 2020-04-20
     */
    public function editForm(array $formData)
    {
        $form = Elm::createForm(Route::buildUrl('merchantAdminEdit')->build());
        $form->setRule([
            Elm::input('real_name', '管理员姓名')->required(),
            Elm::input('phone', '联系电话')
        ]);

        return $form->setTitle('修改信息')->formData($formData);
    }

    /**
     * @param int $id
     * @param array $data
     * @return int
     * @throws DbException
     * @author xaboy
     * @day 2020-04-18
     */
    public function update(int $id, array $data)
    {
        if (isset($data['roles']))
            $data['roles'] = implode(',', $data['roles']);
        return $this->dao->update($id, $data);
    }

}
