package com.liquidnet.service.platform.controller.A_fskfsfs;


import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.JsonNode;
import com.liquidnet.common.cache.redis.util.AbstractRedisUtil;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.common.exception.LiquidnetServiceException;
import com.liquidnet.commons.lang.constant.LnsRegex;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamEntersParam;
import com.liquidnet.service.adam.dto.vo.AdamEntersVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.entity.AdamEnters;
import com.liquidnet.service.adam.entity.AdamUser;
import com.liquidnet.service.adam.mapper.AdamEntersMapper;
import com.liquidnet.service.adam.mapper.AdamUserMapper;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.base.SqlMapping;
import com.liquidnet.service.base.constant.MQConst;
import com.liquidnet.service.platform.utils.QueueUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.IntStream;

/**
 * adam用户相关数据处理
 *
 * @author jiangxiulong
 */
@Api(tags = "临时数据处理")
@RestController
@RequestMapping("fskfsfs")
@Slf4j
public class AdamController {

    @Value("${liquidnet.reviewer.limit.enters:10}")
    private int reviewLimitEnters;
    @Value("${liquidnet.secret.passwd-salt}")
    private String passwdSalt;

    @Autowired
    private AdamUserMapper adamUserMapper;
    @Autowired
    private AdamEntersMapper adamEntersMapper;

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisDataSourceUtil redisDataSourceUtil;
    @Autowired
    QueueUtils queueUtils;
    @Autowired
    JwtValidator jwtValidator;

    @GetMapping("A001")
    @ApiOperation("用户改名 修改入场人信息")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "query", dataType = "String", name = "oldName", value = "旧名字", required = true),
            @ApiImplicitParam(type = "query", dataType = "String", name = "newName", value = "新名字", required = true),
            @ApiImplicitParam(type = "query", dataType = "String", name = "entersPhone", value = "入场人手机号"),
            @ApiImplicitParam(type = "query", dataType = "String", name = "phone", value = "账户手机号", required = true)
    })
    public ResponseDto A001(
            @RequestParam("oldName") String oldName,
            @RequestParam("newName") String newName,
            @RequestParam(value = "entersPhone", required = false) String entersPhone,
            @RequestParam("phone") String phone
    ) {
        AdamUser adamUser = adamUserMapper.selectOne(
                Wrappers.lambdaQuery(AdamUser.class).
                        eq(AdamUser::getMobile, phone)
        );
        String currentUid = adamUser.getUid();
        System.out.println(currentUid);
        LocalDateTime now = LocalDateTime.now();

        AdamEnters adamEnters = adamEntersMapper.selectOne(
                Wrappers.lambdaQuery(AdamEnters.class)
                        .eq(AdamEnters::getName, oldName)
                        .eq(AdamEnters::getUid, currentUid)
        );

        AdamEntersParam parameter = new AdamEntersParam();
        parameter.setType(adamEnters.getType());
        parameter.setIdCard(adamEnters.getIdCard());
        parameter.setEntersId(adamEnters.getEntersId());
        if (null == entersPhone || entersPhone.isEmpty()) {
            parameter.setMobile(adamEnters.getMobile());
        } else {
            parameter.setMobile(entersPhone);
        }
        parameter.setName(newName);
        if (1 == parameter.getType()) {
            this.identityHandler1(currentUid, parameter.getName(), parameter.getIdCard());
        }

        List<AdamEntersVo> vos = this.getEntersVoByUid(currentUid);
        AdamEntersVo updateVo = this.getEntersVoByUidEntersId(vos, parameter.getEntersId());
        updateVo.setType(parameter.getType());
        updateVo.setName(parameter.getName());
        updateVo.setMobile(parameter.getMobile());
        updateVo.setIdCard(parameter.getIdCard());
        updateVo.setUpdatedAt(now);
        long s = System.currentTimeMillis();
        this.setEntersVoByUid(currentUid, this.collectionProcess(vos, parameter.getEntersId(), updateVo));
        if (1 == parameter.getType()) {
            this.setCertification(1, parameter.getIdCard(), parameter.getName());
        }
        log.debug("#RDS耗时:{}ms", System.currentTimeMillis() - s);

        s = System.currentTimeMillis();
        queueUtils.sendMsgByRedis(
                MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_enters.edit",
                        updateVo.getType(), updateVo.getName(), updateVo.getMobile(), updateVo.getIdCard(), updateVo.getIsDefault(), now, updateVo.getEntersId()
                )
        );
        log.debug("#MQ耗时:{}ms", System.currentTimeMillis() - s);

        return ResponseDto.success();
    }

    @GetMapping("A002")
    @ApiOperation("partner自定义固定登陆验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "query", dataType = "String", name = "phone", value = "账户手机号", required = true)
    })
    public ResponseDto A002(
            @RequestParam("phone") String phone
    ) {
        String smsCode = RandomStringUtils.randomNumeric(6);
        redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.VALID_SMS_CODE_MOBILE + phone, smsCode);
        return ResponseDto.success(smsCode);
    }


    @GetMapping("A003")
    @ApiOperation("手机号注销自定义临时验证码")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "query", dataType = "String", name = "phone", value = "账户手机号", required = true),
            @ApiImplicitParam(type = "query", dataType = "Integer", name = "day", value = "临时天数", required = true)
    })
    public ResponseDto A003(
            @RequestParam("phone") String phone,
            @RequestParam("day") Integer day
    ) {
        Integer time = day * 60 * 60 * 24;
        String smsCode = RandomStringUtils.randomNumeric(6);
        redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.VALID_SMS_CODE_MOBILE + phone, smsCode, time);
        return ResponseDto.success(smsCode);
    }

    @PostMapping("A004")
    @ApiOperation("手机号注销账户")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "query", dataType = "String", name = "mobile", value = "账户手机号", required = true),
    })
    public ResponseDto<Object> A004(
            @RequestParam("mobile") String mobile
    ) {
        // 退出登陆
        String key = AdamRedisConst.IDENTITY_MOBILE.concat(mobile);
        String uid = (String) redisDataSourceUtil.getRedisAdamUtil().get(key);
        if (null != uid) {
            redisDataSourceUtil.getRedisAdamUtil().del(jwtValidator.getSsoRedisKey().concat(uid));
        } else {
            uid = adamUserMapper.selectByPhones(mobile).get(0).getUid();
        }

        // 更改状态
        LocalDateTime now = LocalDateTime.now();
        String rk = AdamRedisConst.INFO_USER.concat(uid);
        AdamUserInfoVo vo = (AdamUserInfoVo) redisDataSourceUtil.getRedisAdamUtil().get(rk);
        vo.setState(2);
        vo.setUpdatedAt(now);
        vo.setClosedAt(now);
        redisDataSourceUtil.getRedisAdamUtil().set(rk, vo);
        queueUtils.sendMsgByRedis(
                MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_user.close", now, now, uid)
        );

        return ResponseDto.success();
    }

    @ApiOperation(value = "修改随机6位密码(未验证版本)")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "mobile", value = "手机号"),
    })
    @PostMapping(value = {"A005"})
    public ResponseDto<String> A005(
            @Pattern(regexp = "\\d{11}", message = "手机号格式有误")
            @RequestParam String mobile
    ) {
        String uid = (String) redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.IDENTITY_MOBILE.concat(mobile));
        if (org.apache.commons.lang3.StringUtils.isEmpty(uid)) {
            return ResponseDto.failure(ErrorMapping.get("10003"));
        }

        String rk = AdamRedisConst.INFO_USER.concat(uid);
        AdamUserInfoVo vo = (AdamUserInfoVo) redisDataSourceUtil.getRedisAdamUtil().get(rk);
        if (null == vo) {
            return ResponseDto.failure(ErrorMapping.get("10024"));
        }

        String password = RandomStringUtils.randomNumeric(6);
        String passwdMd5 = DigestUtils.md5DigestAsHex(
                password.toLowerCase().concat(passwdSalt).getBytes(StandardCharsets.UTF_8)
        );
        vo.setPasswd(passwdMd5);
        redisDataSourceUtil.getRedisAdamUtil().set(rk, vo);

        return ResponseDto.success(password);
    }

    @ApiOperation(value = "手机号修改(未验证版本)", notes = "手机号修改会刷新TOKEN，刷新后的TOKEN对应响应参数中[`data`]")
    @ApiImplicitParams({
            @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "mobile", value = "新手机号"),
    })
    @PostMapping(value = {"A006"})
    public ResponseDto<String> A006(
            @Pattern(regexp = "\\d{11}", message = "手机号格式有误")
            @RequestParam String mobile
    ) {
        String rkMobile = AdamRedisConst.IDENTITY_MOBILE.concat(mobile);
        String uid = (String) redisDataSourceUtil.getRedisAdamUtil().get(rkMobile);
        if (!uid.isEmpty()) {
            return ResponseDto.failure(ErrorMapping.get("10009"));
        }

        LocalDateTime now = LocalDateTime.now();
        String rkUid = AdamRedisConst.INFO_USER.concat(uid);
        AdamUserInfoVo beforeUserInfoVo = (AdamUserInfoVo) redisDataSourceUtil.getRedisAdamUtil().get(rkUid);

        redisDataSourceUtil.getRedisAdamUtil().del(rkMobile);
        redisDataSourceUtil.getRedisAdamUtil().set(rkMobile, uid);
        beforeUserInfoVo.setMobile(mobile);
        beforeUserInfoVo.setUpdatedAt(now);
        redisDataSourceUtil.getRedisAdamUtil().set(rkUid, beforeUserInfoVo);

        LinkedList<String> toMqSqls = CollectionUtil.linkedListString();
        LinkedList<Object[]> updateUserInfoObjs = CollectionUtil.linkedListObjectArr(),
                updateUserMobileLocateObjs = CollectionUtil.linkedListObjectArr();

        toMqSqls.add(SqlMapping.get("adam_user.edit.mobile"));
        updateUserInfoObjs.add(new Object[]{mobile, now, uid});

        String locateInfo = (String) redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.LIB_DICT_LOCATE_MOBILE.concat(mobile.substring(0, 7)));
        String[] mobileLocateArr = StringUtils.isEmpty(locateInfo) ? null : locateInfo.split("\\|");
        toMqSqls.add(SqlMapping.get("adam_user_mobile_locate.modify_mobile"));
        updateUserMobileLocateObjs.add(new Object[]{
                mobile,
                ArrayUtils.isEmpty(mobileLocateArr) ? null : mobileLocateArr[2],
                now, uid
        });

        queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.gets(toMqSqls, updateUserInfoObjs, updateUserMobileLocateObjs)
        );

        Map<String, Object> claimsMap = CollectionUtil.mapStringObject();
        claimsMap.put(CurrentUtil.TOKEN_SUB, beforeUserInfoVo.getUid());
        claimsMap.put(CurrentUtil.TOKEN_MOBILE, beforeUserInfoVo.getMobile());
        claimsMap.put(CurrentUtil.TOKEN_NICKNAME, beforeUserInfoVo.getNickname());
        claimsMap.put(CurrentUtil.TOKEN_TYPE, CurrentUtil.TOKEN_TYPE_VAL_USER);
        claimsMap.put(CurrentUtil.TOKEN_UCREATED, DateUtil.Formatter.yyyyMMddHHmmssTrim.format(beforeUserInfoVo.getCreateAt()));

        String token = jwtValidator.create(claimsMap);

        redisDataSourceUtil.getRedisAdamUtil().set(
                jwtValidator.getSsoRedisKey().concat(beforeUserInfoVo.getUid()),
                DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)),
                jwtValidator.getExpireTtl() * 60
        );

        return ResponseDto.success(token);
    }

    @ApiOperation(value = "添加入场人", notes = "～")
    @PostMapping(value = {"A007"})
    public ResponseDto<String> A007(
            @RequestBody @Valid AdamEntersParam parameter
    ) {
        LocalDateTime now = LocalDateTime.now();
        String rkMobile = AdamRedisConst.IDENTITY_MOBILE.concat(parameter.getMobile());
        String uid = (String) redisDataSourceUtil.getRedisAdamUtil().get(rkMobile);
        if (StringUtils.isEmpty(uid)) {
            return ResponseDto.failure("该手机号未注册");
        }

        switch (parameter.getType()) {// 证件类型:1-大陆身份证,2-港澳通行证,3-台胞证,4-护照,5-军官证
            case 1:
                if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_HANZI, parameter.getName())) {
                    return ResponseDto.failure(ErrorMapping.get("10103"));
                }
                if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_ID_CARD_REF, parameter.getIdCard())) {
                    return ResponseDto.failure(ErrorMapping.get("10104"));
                }
                break;
            case 2:
                if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_ID_CARD_HM, parameter.getIdCard())) {
                    return ResponseDto.failure(ErrorMapping.get("10107"));
                }
                break;
            case 3:
                if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_ID_CARD_TW, parameter.getIdCard())) {
                    return ResponseDto.failure(ErrorMapping.get("10108"));
                }
                break;
            case 4:
                break;
            case 5:
                if (!java.util.regex.Pattern.matches(LnsRegex.Valid.CN_ID_CARD_MO, parameter.getIdCard())) {
                    return ResponseDto.failure(ErrorMapping.get("10111"));
                }
                break;
        }

        String rkEnters = AdamRedisConst.INFO_ENTERS.concat(uid);
        List<AdamEntersVo> vos = (List<AdamEntersVo>) redisDataSourceUtil.getRedisAdamUtil().get(rkEnters);
        if (!CollectionUtils.isEmpty(vos)) {
            Optional<AdamEntersVo> any = vos.stream().filter(r -> (r.getIdCard().equals(parameter.getIdCard())) && r.getType().equals(parameter.getType())).findAny();
            if (any.isPresent()) {
                return ResponseDto.failure(ErrorMapping.get("10019"));
            }

            if (reviewLimitEnters > 0 && vos.size() >= reviewLimitEnters) {
                return ResponseDto.failure(ErrorMapping.get("10025"));
            }
        }


        if (1 == parameter.getType()) {
            this.identityHandler1(uid, parameter.getName(), parameter.getIdCard());
        }
        AdamEntersVo vo = AdamEntersVo.getNew();
        BeanUtils.copyProperties(parameter, vo);
        vo.setEntersId(IDGenerator.nextSnowId());
        vo.setUid(uid);
        vo.setIsDefault(CollectionUtils.isEmpty(vos));
        vo.setState(1);
        vo.setCreatedAt(now);

        if (null == vos) {
            vos = new ArrayList<>();
        }
        vos.add(0, vo);
        redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_ENTERS + uid, vos);

        queueUtils.sendMsgByRedis(MQConst.AdamQueue.SQL_UCENTER.getKey(),
                SqlMapping.get("adam_enters.add",
                        vo.getEntersId(), uid, vo.getType(), vo.getName(), "", vo.getIdCard(), vo.getIsDefault(), vo.getState(), now)
        );

        return ResponseDto.success(vo.getEntersId());
    }

    /**
     * 身份证实名处理
     *
     * @param uid
     * @param name
     * @param idCard
     */
    public void identityHandler1(String uid, String name, String idCard) {
        int rst = this.isCertification(1, idCard, name);
        switch (rst) {
            case -1:// 本地不存在
                if (this.isCertificationJunk(1, idCard, name)) {
                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }

                String respStr = IdentityUtils.aliThird(name, idCard), respErrorCode = null;
                JsonNode respJNode = JsonUtils.fromJson(respStr, JsonNode.class);
                if (null == respJNode || !"0".equals(respErrorCode = String.valueOf(respJNode.get("error_code")))) {
                    log.info("###实名认证失败[{}]", respStr);

//                    this.setCertificationJunk(1, idCard, name);
                    if (!StringUtils.isEmpty(respErrorCode) && org.apache.commons.lang3.StringUtils.indexOf("3000290033", respErrorCode) < 0) {
                        // 认证服务商'30002'、'90033'为运营商导致的失败，这里不做缓存标记
                        this.setCertificationJunk(1, idCard, name);
                    }

                    ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                    throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
                }

                this.setCertification(1, idCard, name);
                break;
            case 0:// 本地存在，验证不通过
                ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10102");
                throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
            case 1:// 本地存在，验证通过
                break;
        }
    }

    /**
     * 目前只针对身份证类型三方服务认证成功的标记
     *
     * @param idType
     * @param idNo
     * @param idName
     * @return
     */
    public int isCertification(int idType, String idNo, String idName) {
        redisDataSourceUtil.getRedisAdamUtil().del(AdamRedisConst.INFO_CERTIFICATION + idType + idNo);
        String o = (String) redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.INFO_CERTIFICATION + idType + idNo);
        if (StringUtils.isEmpty(o)) {
            return -1;
        }
        return o.equals(idName) ? 1 : 0;
//        return !StringUtils.isEmpty(o) && o.equals(idName);
    }

    /**
     * 目前只针对身份证类型三方服务认证成功的标记
     *
     * @param idType
     * @param idNo
     * @param idName
     * @return
     */
    public boolean setCertification(int idType, String idNo, String idName) {
        return redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_CERTIFICATION + idType + idNo, idName);
    }

    public boolean isCertificationJunk(int idType, String idNo, String idName) {
        String o = (String) redisDataSourceUtil.getRedisAdamUtil().get(AdamRedisConst.INFO_CERTIFICATION_JUNK + idType + idNo);
        return !StringUtils.isEmpty(o) && o.equals(idName);
    }

    public boolean setCertificationJunk(int idType, String idNo, String idName) {
        return redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_CERTIFICATION_JUNK + idType + idNo, idName, 604800);
    }

    public List<AdamEntersVo> getEntersVoByUid(String uid) {
        String rk = AdamRedisConst.INFO_ENTERS.concat(uid);
        long s = System.currentTimeMillis();
        AbstractRedisUtil redisAdamUtil = redisDataSourceUtil.getRedisAdamUtil();
        ArrayList<AdamEntersVo> vos = (ArrayList<AdamEntersVo>) redisAdamUtil.get(rk);
        log.debug("#RDM耗时:{}ms", System.currentTimeMillis() - s);
        return vos;
    }

    public AdamEntersVo getEntersVoByUidEntersId(List<AdamEntersVo> vos, String entersId) {
        return CollectionUtils.isEmpty(vos) ? null : vos.stream().filter(r -> r.getEntersId().equals(entersId)).findAny().orElse(null);
    }

    public boolean setEntersVoByUid(String uid, List<AdamEntersVo> list) {
        return redisDataSourceUtil.getRedisAdamUtil().set(AdamRedisConst.INFO_ENTERS.concat(uid), list);
    }

    private List<AdamEntersVo> collectionProcess(List<AdamEntersVo> vos, String replaceId, AdamEntersVo updateVo) {
        long s = System.currentTimeMillis();
        int idx = IntStream.range(0, vos.size())
                .filter(i -> vos.get(i).getEntersId().equals(replaceId))
                .findFirst().orElse(-1);
        if (idx == -1) {
            ErrorMapping.ErrorMessage errorMessage = ErrorMapping.get("10017");
            throw new LiquidnetServiceException(errorMessage.getCode(), errorMessage.getMessage());
        }
        vos.set(idx, updateVo);
        log.debug("#collect.process耗时:{}ms", System.currentTimeMillis() - s);
        return vos;
    }

}
