package com.zanxiang.game.module.sdk.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.zanxiang.game.module.base.ServerInfo;
import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
import com.zanxiang.game.module.base.rpc.IUserRpc;
import com.zanxiang.game.module.mybatis.entity.Game;
import com.zanxiang.game.module.mybatis.entity.User;
import com.zanxiang.game.module.sdk.constant.RedisKeyConstant;
import com.zanxiang.game.module.sdk.enums.ExpireTimeEnum;
import com.zanxiang.game.module.sdk.enums.SmsTypeEnum;
import com.zanxiang.game.module.sdk.pojo.dto.UserDTO;
import com.zanxiang.game.module.sdk.pojo.param.BindPhoneParam;
import com.zanxiang.game.module.sdk.pojo.param.SmsCheckParam;
import com.zanxiang.game.module.sdk.pojo.param.SmsSendParam;
import com.zanxiang.game.module.sdk.pojo.param.UserData;
import com.zanxiang.game.module.sdk.service.IGameService;
import com.zanxiang.game.module.sdk.service.ISmsService;
import com.zanxiang.game.module.sdk.service.IUserService;
import com.zanxiang.game.module.sdk.util.RedisUtil;
import com.zanxiang.game.module.sdk.util.RegexUtil;
import com.zanxiang.module.sms.pojo.SendResult;
import com.zanxiang.module.sms.service.impl.AliSmsService;
import com.zanxiang.module.util.exception.BaseException;
import com.zanxiang.module.util.pojo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author : lingfeng
 * @time : 2022-06-09
 * @description : 手机短信逻辑
 */
@Slf4j
@Service
public class SmsServiceImpl implements ISmsService {

    @DubboReference(providedBy = ServerInfo.SERVER_DUBBO_NAME)
    private IUserRpc userRpc;

    @Autowired
    private IUserService userService;

    @Autowired
    private RedisUtil<String> redisUtil;

    @Autowired
    private AliSmsService aliSmsService;

    @Autowired
    private IGameService gameService;

    @Override
    public Map<String, Object> getBindPhone(UserData userData) {
        User user = userService.getById(userData.getUserId());
        if (user == null) {
            throw new BaseException("参数错误, 用户信息不存在");
        }
        Map<String, Object> resultMap = new HashMap<>(3);
        //未绑定手机号或者绑定时间超过1个月
        if (Strings.isBlank(user.getMobile()) || user.getMobileBindTime() == null
                || ChronoUnit.DAYS.between(user.getMobileBindTime(), LocalDateTime.now()) >= 30) {
            resultMap.put("isBindPhone", Boolean.FALSE);
            Game game = gameService.getById(user.getGameId());
            //诸神-攻守CP前端要求未绑定的时候, 返回空串
            if (Objects.equals(game.getSuperGameId(), 2L)) {
                resultMap.put("phone", "");
                resultMap.put("mobileBindTime", "");
            }
            return resultMap;
        }
        //已绑定手机号
        resultMap.put("isBindPhone", Boolean.TRUE);
        resultMap.put("phone", user.getShowPhoneNum());
        resultMap.put("mobileBindTime", user.getMobileBindTime());
        return resultMap;
    }

    @Override
    public Map<String, Object> bindPhoneSend(String phone, UserData userData) {
        //验证手机号
        if (!RegexUtil.checkPhone(phone)) {
            return this.buildResultMap(Boolean.FALSE, "手机号不合法, 请重新输入");
        }
        //玩家信息
        User user = userService.getById(userData.getUserId());
        if (user == null) {
            throw new BaseException("参数错误, 用户信息不存在");
        }
        //判断玩家是否已经绑定手机号, 且绑定时间未超过1个月
        if (Strings.isNotBlank(user.getMobile()) && user.getMobileBindTime() != null
                && ChronoUnit.DAYS.between(user.getMobileBindTime(), LocalDateTime.now()) < 30) {
            return this.buildResultMap(Boolean.FALSE, "玩家已经绑定过手机号, 不可重复操作");
        }
        //校验手机号是否已经绑定了其他账号
        if (userService.count(new LambdaQueryWrapper<User>()
                .eq(User::getGameId, userData.getGameId())
                .eq(User::getMobile, phone)
        ) > 0) {
            return this.buildResultMap(Boolean.FALSE, "手机号已被其他账号绑定, 请联系客服");
        }
        //验证码缓存key
        String key = this.smsKey(phone, SmsTypeEnum.SMS_BIND.getType());
        //判断是否已经发送
        if (Strings.isNotBlank(redisUtil.getCache(key))) {
            return this.buildResultMap(Boolean.FALSE, "验证码已经发送, 请勿重复操作");
        }
        //发送验证码
        String randomCode = this.randomCode();
        SendResult sendResult = aliSmsService.sendCode(randomCode, phone);
        //发送失败
        if (!sendResult.isSuccess()) {
            return this.buildResultMap(Boolean.FALSE, "验证码发送失败, 请稍后重试或者联系客服");
        }
        //发送成功设置缓存, 时效5分钟
        redisUtil.setCache(key, randomCode, ExpireTimeEnum.FIVE_MIN.getTime());
        return this.buildResultMap(Boolean.TRUE, "发送成功");
    }

    private Map<String, Object> buildResultMap(Boolean sendResult, String msg) {
        Map<String, Object> resultMap = new HashMap<>(2);
        resultMap.put("result", sendResult);
        resultMap.put("msg", msg);
        return resultMap;
    }

    @Override
    public Map<String, Object> bindPhoneCheck(BindPhoneParam param, UserData userData) {
        //验证码验证
        HttpStatusEnum statusEnum = this.smsCheck(SmsTypeEnum.SMS_BIND.getType(), param.getMobile(), param.getCode());
        if (!Objects.equals(statusEnum, HttpStatusEnum.SUCCESS)) {
            return this.buildResultMap(Boolean.FALSE, statusEnum.getMsg());
        }
        //更新用户手机号
        userService.update(new LambdaUpdateWrapper<User>()
                .set(User::getMobile, param.getMobile())
                .set(User::getMobileBindTime, LocalDateTime.now())
                .set(User::getUpdateTime, LocalDateTime.now())
                .eq(User::getId, userData.getUserId()));
        //通知游戏后台转端初始化
        userRpc.appletToOther(userData.getUserId(), userData.getGameId(), param.getMobile());
        //返回结果
        return this.buildResultMap(Boolean.TRUE, "绑定成功");
    }

    @Override
    public ResultVO<Boolean> smsSend(SmsSendParam smsSendParam, UserData userData) {
        //类型
        Integer type = smsSendParam.getType();
        //手机号码
        String mobile = smsSendParam.getMobile();
        //根据手机号获取用户信息
        UserDTO userDTO = userService.getUserInfoByMobile(mobile, userData.getGameId());
        //找回密码获取
        if (Objects.equals(type, SmsTypeEnum.SMS_FIND_PWD.getType())) {
            //手机号对应用户信息不存在
            if (userDTO == null) {
                return ResultVO.fail(HttpStatusEnum.USERNAME_NOT_EXISTS.getMsg());
            }
        }
        //验证手机号
        if (!RegexUtil.checkPhone(mobile)) {
            return ResultVO.fail(HttpStatusEnum.PHONE_ERROR.getMsg());
        }
        //验证类型
        SmsTypeEnum smsTypeEnum = SmsTypeEnum.getByType(type);
        if (smsTypeEnum == null) {
            return ResultVO.fail(HttpStatusEnum.CODE_TYPE_ERROR.getMsg());
        }
        //判断是否已经发送
        String cache = redisUtil.getCache(this.smsKey(mobile, type));
        if (Strings.isNotBlank(cache)) {
            return ResultVO.fail(HttpStatusEnum.PHONE_HAS_SEND.getMsg());
        }
        //发送验证码
        String randomCode = this.randomCode();
        SendResult sendResult = aliSmsService.sendCode(randomCode, mobile);
        //发送成功设置缓存, 时效5分钟
        if (sendResult.isSuccess()) {
            redisUtil.setCache(this.smsKey(mobile, type), randomCode, ExpireTimeEnum.FIVE_MIN.getTime());
        }
        return ResultVO.ok(Boolean.TRUE);
    }

    @Override
    public ResultVO<Boolean> smsCheck(SmsCheckParam smsCheckParam) {
        //校验短信验证码
        HttpStatusEnum httpStatusEnum = this.smsCheck(smsCheckParam.getType(), smsCheckParam.getMobile(), smsCheckParam.getCode());
        //验证失败
        if (!Objects.equals(httpStatusEnum, HttpStatusEnum.SUCCESS)) {
            return ResultVO.fail(httpStatusEnum.getMsg());
        }
        //返回
        return ResultVO.ok(Boolean.TRUE);
    }

    @Override
    public HttpStatusEnum smsCheck(Integer type, String mobile, String code) {
        //验证类型
        SmsTypeEnum smsTypeEnum = SmsTypeEnum.getByType(type);
        if (smsTypeEnum == null) {
            return HttpStatusEnum.CODE_TYPE_ERROR;
        }
        //验证手机号
        if (!RegexUtil.checkPhone(mobile)) {
            return HttpStatusEnum.PHONE_ERROR;
        }
        //获取缓存中的验证码
        String smsKey = this.smsKey(mobile, type);
        String cache = redisUtil.getCache(smsKey);
        //验证码已过期
        if (Strings.isBlank(cache)) {
            return HttpStatusEnum.CODE_IS_EXPIRED;
        }
        //验证码错误
        if (!Objects.equals(cache, code)) {
            return HttpStatusEnum.CODE_ERROR;
        }
        //验证成功, 删除缓存
        redisUtil.deleteCache(smsKey);
        //返回成功
        return HttpStatusEnum.SUCCESS;
    }

    private String randomCode() {
        return RandomUtil.randomNumbers(6);
    }

    private String smsKey(String mobile, Integer type) {
        return RedisKeyConstant.SMS_PHONE_KEY + "_" + mobile + "_" + type;
    }
}
