package com.ruoyi.bst.realName.service.impl;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import com.ruoyi.bst.realName.domain.RealName;
import com.ruoyi.bst.realName.domain.RealNameQuery;
import com.ruoyi.bst.realName.domain.RealNameVO;
import com.ruoyi.bst.realName.domain.dto.RealNameTowEleDTO;
import com.ruoyi.bst.realName.domain.enums.RealNameStatus;
import com.ruoyi.bst.realName.domain.enums.RealNameType;
import com.ruoyi.bst.realName.domain.face.Face;
import com.ruoyi.bst.realName.domain.refresh.RealNameRefreshVO;
import com.ruoyi.bst.realName.domain.vo.RealNameFaceVO;
import com.ruoyi.bst.realName.mapper.RealNameMapper;
import com.ruoyi.bst.realName.service.RealNameService;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.qiniu.QiNiuUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.common.valid.face.FaceResponseData;
import com.ruoyi.common.valid.face.FaceUtils;
import com.ruoyi.common.valid.face.enums.FaceIncorrect;
import com.ruoyi.common.valid.face.enums.FaceScoreResult;
import com.ruoyi.common.valid.liveness.LivenessResponseQueryData;
import com.ruoyi.common.valid.liveness.LivenessResponseTokenData;
import com.ruoyi.common.valid.liveness.LivenessUtils;
import com.ruoyi.common.valid.liveness.enums.LivenessQueryResult;
import com.ruoyi.common.valid.towEle.TwoEleUtil;
import com.ruoyi.common.valid.towEle.vo.IDResponse;
import com.ruoyi.system.user.domain.enums.UserAuthType;
import com.ruoyi.system.user.service.UserService;
import com.ruoyi.system.user.utils.UserUtil;

/**
 * 实名认证信息Service业务层处理
 *
 * @author ruoyi
 * @date 2025-03-29
 */
@Service
public class RealNameServiceImpl implements RealNameService
{
    @Autowired
    private RealNameMapper realNameMapper;

    // 活体检测返回地址
    public static final String LIVENESS_RETURN_URL = SpringUtils.getRequiredProperty("liveness.returnUrl");

    @Autowired
    private RedisCache redisCache;
    @Autowired
    private UserService userService;
    @Autowired
    private TransactionTemplate transactionTemplate;

    /**
     * 查询实名认证信息
     *
     * @param id 实名认证信息主键
     * @return 实名认证信息
     */
    @Override
    public RealNameVO selectRealNameById(Long id)
    {
        return realNameMapper.selectRealNameById(id);
    }

    /**
     * 查询实名认证信息列表
     *
     * @param realName 实名认证信息
     * @return 实名认证信息
     */
    @Override
    public List<RealNameVO> selectRealNameList(RealNameQuery realName)
    {
        return realNameMapper.selectRealNameList(realName);
    }

    /**
     * 新增实名认证信息
     *
     * @param realName 实名认证信息
     * @return 结果
     */
    @Override
    public int insertRealName(RealName realName)
    {
        realName.setCreateTime(DateUtils.getNowDate());
        return realNameMapper.insertRealName(realName);
    }

    /**
     * 批量删除实名认证信息
     *
     * @param ids 需要删除的实名认证信息主键
     * @return 结果
     */
    @Override
    public int deleteRealNameByIds(List<Long> ids)
    {
        return realNameMapper.deleteRealNameByIds(ids);
    }

    /**
     * 删除实名认证信息信息
     *
     * @return 结果
     */
    @Override
    public int deleteRealNameById(Long id)
    {
        return realNameMapper.deleteRealNameById(id);
    }

    @Override
    public RealNameFaceVO faceRealName(RealName realName) {
        UserVO user = userService.selectUserById(realName.getUserId());
        ServiceUtil.assertion(user == null,"用户信息不存在");
        ServiceUtil.assertion(UserUtil.hasAuth(user, UserAuthType.REAL_NAME.getCode()),"用户已实名认证");

        // 获取活体检测token
        LivenessResponseTokenData tokenData = LivenessUtils.getToken(LIVENESS_RETURN_URL, null, null);
        ServiceUtil.assertion(tokenData == null || StringUtils.isBlank(tokenData.getToken()), "获取活体检测token失败");

        // TODO 改为存入数据库，有效期10分钟
        String cacheKey = CacheConstants.LIVENESS_TOKEN + realName.getUserId();
        Face face = new Face();
        face.setUserId(realName.getUserId());
        face.setToken(tokenData.getToken());
        face.setName(realName.getUserName());
        face.setMobile(realName.getMobile());
        face.setIdCard(realName.getIdCard());
        redisCache.setCacheObject(cacheKey, face, 10, TimeUnit.MINUTES);

        // 返回数据
        RealNameFaceVO vo = new RealNameFaceVO();
        vo.setFaceToken(tokenData.getToken());
        vo.setJumpUrl(LivenessUtils.JUMP_URL + "?token=" + tokenData.getToken());
        return vo;
    }

    /**
     * 刷新用戶认证信息
     * @param userId
     * @return
     */
    @Override
    public RealNameRefreshVO refreshRealName(Long userId) {
        RealNameRefreshVO vo = new RealNameRefreshVO();
        vo.setPass(false);

        if (userId == null) {
            return vo;
        }

        // 通过userId获取用户提交的信息
        String cacheKey = CacheConstants.LIVENESS_TOKEN + userId;
        Face face = redisCache.getCacheObject(cacheKey);
        ServiceUtil.assertion(face == null, "token不存在");

        // 获取活体图像及三要素信息
        LivenessResponseQueryData livenessResult = LivenessUtils.queryResult(face.getToken());
        ServiceUtil.assertion(livenessResult == null, "活体信息不存在");
        ServiceUtil.assertion(!LivenessQueryResult.PASS.getResult().equals(livenessResult.getResult()), "活体检测未通过");
        ServiceUtil.assertion(StringUtils.isBlank(livenessResult.getFaceImage()), "活体检测未通过");


        // 将Base64上传到七牛云中
        String imageUrl = livenessResult.getFaceImage();
        try {
            imageUrl = QiNiuUtils.DOMAIN + "/" + QiNiuUtils.uploadBase64(imageUrl);
        } catch (Exception e) {
            throw new ServiceException("上传文件失败:" + e.getMessage());
        }

        // 判断活体是否匹配
        FaceResponseData faceIdb = FaceUtils.getFaceIdb(face.getIdCard(), face.getName(), livenessResult.getFaceImage(), null);
        ServiceUtil.assertion(faceIdb == null, "人证比对数据不存在");

        boolean isSuccess = FaceIncorrect.isSuccess(faceIdb.getIncorrect());
        boolean isScoreSuccess = FaceScoreResult.isSame(faceIdb.getScore());

        // 通过，构造实名认证表
        RealName realName = new RealName();
        realName.setUserId(userId);
        realName.setUserName(face.getName());
        realName.setIdCard(face.getIdCard());
        realName.setMobile(face.getMobile());
        realName.setScore(faceIdb.getScore());
        realName.setPicture(imageUrl);
        realName.setStatus(isSuccess && isScoreSuccess ? RealNameStatus.REAL.getCode() : RealNameStatus.FAILED.getCode());
        realName.setType(RealNameType.FACE.getCode());
        realName.setResult(isSuccess && isScoreSuccess ? "认证成功" : "人证比对未通过，分数：" + faceIdb.getScore());

        int result = this.handleNormalRealName(realName, cacheKey);
    
        vo.setPass(isSuccess && isScoreSuccess);
        ServiceUtil.assertion(result != 1 || !vo.getPass(), "实名认证失败");

        return vo;
    }

    @Override
    public int twoElementRealName(RealNameTowEleDTO dto) {
        // 判断用户是否已经实名认证
        UserVO user = userService.selectUserById(dto.getUserId());
        ServiceUtil.assertion(user == null,"用户信息不存在");
        ServiceUtil.assertion(UserUtil.hasAuth(user, UserAuthType.REAL_NAME.getCode()), "用户已实名认证，请勿重复实名");

        IDResponse idRes = TwoEleUtil.verifyIdentity(dto.getIdCard(), dto.getUserName());
        ServiceUtil.assertion(idRes == null, "二要素实名认证返回为空，请联系管理员续费");

        boolean isSuccess = IDResponse.isSuccess(idRes);
        
        // 保存实名信息
        RealName realName = new RealName();
        realName.setUserId(dto.getUserId());
        realName.setUserName(dto.getUserName());
        realName.setIdCard(dto.getIdCard());
        realName.setStatus(isSuccess ? RealNameStatus.REAL.getCode() : RealNameStatus.FAILED.getCode());
        realName.setType(RealNameType.TWO_ELEMENT.getCode());
        realName.setResult(isSuccess ? "认证成功" : idRes.getReason());

        int result = this.handleNormalRealName(realName, null);
        ServiceUtil.assertion(result != 1, "实名认证失败");

        ServiceUtil.assertion(!isSuccess, "二要素实名认证未通过");

        return result;
    }

    private int handleNormalRealName(RealName realName, String cacheKey) {
        // 数据库操作
        Integer result = transactionTemplate.execute(status -> {
            // 插入实名认证表
            int insert = this.insertRealName(realName);
            ServiceUtil.assertion(insert != 1, "新增实名认证失败");

            if (realName.getStatus().equals(RealNameStatus.REAL.getCode())) {
                // 修改用户实名信息
                int update = userService.updateUserRealName(realName);
                ServiceUtil.assertion(update != 1, "更新用户信息失败");

                // 更新用户已认证类型
                int auth = userService.addAuthType(realName.getUserId(), UserAuthType.REAL_NAME);
                ServiceUtil.assertion(auth != 1, "更新用户已认证类型失败");
            }

            // 删除缓存
            if (cacheKey != null) {
                redisCache.deleteObject(cacheKey);
            }

            return insert;
        });

        return result == null ? 0 : result;
    }
}
