package com.ruoyi.ss.access.service.impl;

import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.core.redis.RedisLock;
import com.ruoyi.common.core.redis.enums.RedisLockKey;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.access.domain.Access;
import com.ruoyi.ss.access.domain.AccessQuery;
import com.ruoyi.ss.access.domain.AccessVO;
import com.ruoyi.ss.access.domain.vo.AccessApplyVO;
import com.ruoyi.ss.access.mapper.AccessMapper;
import com.ruoyi.ss.access.service.AccessService;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.user.service.ISmUserService;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 第三方API秘钥对Service业务层处理
 *
 * @author ruoyi
 * @date 2024-07-17
 */
@Service
public class AccessServiceImpl implements AccessService
{
    @Autowired
    private AccessMapper accessMapper;

    @Value("${access.maxCount}")
    private Integer maxCount;

    @Autowired
    private RedisLock redisLock;

    @Autowired
    private ISmUserService userService;

    @Autowired
    private RedisCache redisCache;

    /**
     * 查询第三方API秘钥对
     *
     * @param accessId 第三方API秘钥对主键
     * @return 第三方API秘钥对
     */
    @Override
    public AccessVO selectAccessByAccessId(Long accessId)
    {
        return accessMapper.selectAccessByAccessId(accessId);
    }

    /**
     * 查询第三方API秘钥对列表
     *
     * @param access 第三方API秘钥对
     * @return 第三方API秘钥对
     */
    @Override
    public List<AccessVO> selectAccessList(AccessQuery access)
    {
        return accessMapper.selectAccessList(access);
    }

    /**
     * 新增第三方API秘钥对
     *
     * @param access 第三方API秘钥对
     * @return 结果
     */
    @Override
    public int insertAccess(Access access)
    {
        access.setCreateTime(DateUtils.getNowDate());
        return accessMapper.insertAccess(access);
    }

    /**
     * 修改第三方API秘钥对
     *
     * @param data 第三方API秘钥对
     * @return 结果
     */
    @Override
    public int updateAccess(Access data)
    {
        int update = accessMapper.updateAccess(data);
        if (update == 1) {
            // 刷新缓存
            AccessVO access = selectAccessByAccessId(data.getAccessId());
            this.deleteCache(access.getAccessKey());
        }
        return update;
    }

    /**
     * 批量删除第三方API秘钥对
     *
     * @param accessIds 需要删除的第三方API秘钥对主键
     * @return 结果
     */
    @Override
    public int deleteAccessByAccessIds(List<Long> accessIds)
    {
        if (CollectionUtils.isEmptyElement(accessIds)) {
            return 0;
        }
        AccessQuery query = new AccessQuery();
        query.setAccessIds(accessIds);
        List<AccessVO> accessList = selectAccessList(query);
        int delete = accessMapper.deleteAccessByAccessIds(accessIds);

        // 清除缓存
        for (AccessVO access : accessList) {
            this.deleteCache(access.getAccessKey());
        }

        return delete;
    }

    /**
     * 删除第三方API秘钥对信息
     *
     * @param accessId 第三方API秘钥对主键
     * @return 结果
     */
    @Override
    public int deleteAccessByAccessId(Long accessId)
    {
        return accessMapper.deleteAccessByAccessId(accessId);
    }

    @Override
    public AccessApplyVO apply(Long userId) {
        if (userId == null) {
            return null;
        }
        ServiceUtil.assertion(!redisLock.lock(RedisLockKey.APPLY_ACCESS, userId), "请勿重复提交申请");
        try {
            int count = this.selectCountByUserId(userId);
            ServiceUtil.assertion(count >= maxCount, "用户密钥对超过最大数量，最大允许数量：" + maxCount);

            // 生成原始密钥对
            Access access = this.genAccess(userId);
            AccessApplyVO result = new AccessApplyVO(access);

            // 加密秘钥
            access.setAccessSecret(SecurityUtils.encryptPassword(access.getAccessSecret()));
            // 插入加密后的秘钥
            int insert = this.insertAccess(access);
            ServiceUtil.assertion(insert != 1, "生成密钥对失败");

            // 返回未加密的密钥对
            return result;
        } finally {
            redisLock.unlock(RedisLockKey.APPLY_ACCESS, userId);
        }
    }

    // 生成一个密钥对
    private Access genAccess(Long userId) {
        if (userId == null) {
            return null;
        }
        Access access = new Access();
        access.setUserId(userId);
        access.setAccessKey(StringUtil.valueOf(SnowFlakeUtil.newId()));
        access.setAccessSecret(SecurityUtils.genSecret(64));
        return access;
    }

    @Override
    public int selectCountByUserId(Long userId) {
        if (userId == null) {
            return 0;
        }
        return accessMapper.selectCountByUserId(userId);
    }

    @Override
    public SmUserVO selectUserByAccess(String accessKey) {
        AccessVO access = this.selectByAccessKey(accessKey);
        if (access == null) {
            return null;
        }
        return userService.selectSmUserByUserId(access.getUserId());
    }

    @Override
    public AccessVO selectByAccessKey(String accessKey) {
        return accessMapper.selectByAccessKey(accessKey);
    }

    @Override
    public boolean deleteCache(String accessKey) {
        if (StringUtils.isBlank(accessKey)) {
            return false;
        }
        return redisCache.deleteObject(CacheConstants.ACCESS_KEY_PREFIX + accessKey);
    }

    @Override
    public String reset(Long accessId) {
        ServiceUtil.assertion(accessId == null, "秘钥ID不允许为空");
        AccessVO old = selectAccessByAccessId(accessId);
        ServiceUtil.assertion(old == null, "旧秘钥不存在");

        String secret = SecurityUtils.genSecret(64);
        Access access = new Access();
        access.setAccessId(accessId);
        access.setAccessSecret(SecurityUtils.encryptPassword(secret));

        int update = accessMapper.updateAccess(access);
        ServiceUtil.assertion(update != 1, "重置秘钥失败");

        // 清除缓存
        this.deleteCache(old.getAccessKey());

        return secret;
    }
}
