package com.sunyard.chsm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.enums.EnableStatus;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.mapper.ApplicationMapper;
import com.sunyard.chsm.mapper.KeyCsrMapper;
import com.sunyard.chsm.mapper.KeyInfoMapper;
import com.sunyard.chsm.mapper.KeyTemplateMapper;
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
import com.sunyard.chsm.model.dto.KeyInfoDTO;
import com.sunyard.chsm.model.entity.Application;
import com.sunyard.chsm.model.entity.KeyCsr;
import com.sunyard.chsm.model.entity.KeyInfo;
import com.sunyard.chsm.model.entity.KeyRecord;
import com.sunyard.chsm.model.entity.KeyTemplate;
import com.sunyard.chsm.sdf.SdfApiService;
import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.service.KeyInfoService;
import com.sunyard.chsm.utils.JsonUtils;
import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.cert.CommonCertUtils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author liulu
 * @since 2024/10/23
 */
@Slf4j
@Service
@Transactional
public class KeyInfoServiceImpl implements KeyInfoService {

    @Resource
    private KeyCsrMapper keyCsrMapper;
    @Resource
    private KeyInfoMapper keyInfoMapper;
    @Resource
    private SpKeyRecordMapper spKeyRecordMapper;
    @Resource
    private KeyTemplateMapper keyTemplateMapper;
    @Resource
    private ApplicationMapper applicationMapper;
    @Resource
    private SdfApiService sdfApiService;


    @Override
    public Page<KeyInfoDTO.KeyView> selectPageList(KeyInfoDTO.Query query) {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<KeyInfo> wrapper = new LambdaQueryWrapper<KeyInfo>()
                .eq(StringUtils.hasText(query.getKeyType()), KeyInfo::getKeyType, query.getKeyType())
                .eq(Objects.nonNull(query.getAppId()), KeyInfo::getApplicationId, query.getAppId())
//                .eq(StringUtils.hasText(query.getStatus()), KeyInfo::getStatus, query.getStatus())
//                .eq(KeyInfo::getDeleted, false)
                .orderByDesc(KeyInfo::getCreateTime);
        if (StringUtils.hasText(query.getStatus())) {
            KeyStatus queryStatus = KeyStatus.of(query.getStatus());
            if (KeyStatus.WAIT_ENABLED == queryStatus) {
                wrapper.eq(KeyInfo::getStatus, KeyStatus.ENABLED.getCode())
                        .gt(KeyInfo::getEffectiveTime, now);
            } else if (KeyStatus.EXPIRED == queryStatus) {
                wrapper.eq(KeyInfo::getStatus, KeyStatus.ENABLED.getCode())
                        .lt(KeyInfo::getExpiredTime, now);
            } else if (KeyStatus.ENABLED == queryStatus) {
                wrapper.eq(KeyInfo::getStatus, KeyStatus.ENABLED.getCode())
                        .lt(KeyInfo::getEffectiveTime, now)
                        .gt(KeyInfo::getExpiredTime, now);
            } else {
                wrapper.eq(KeyInfo::getStatus, query.getStatus());
            }
        }

        IPage<KeyInfo> page = keyInfoMapper.selectPage(
                new Page<>(query.getPageNumber(), query.getPageSize()),
                wrapper
        );
        List<KeyInfo> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        }

        List<Long> appIds = records.stream().map(KeyInfo::getApplicationId).collect(Collectors.toList());
        Map<Long, String> appNameMap = applicationMapper.selectBatchIds(appIds)
                .stream().collect(Collectors.toMap(Application::getId, Application::getName));

        List<KeyInfoDTO.KeyView> viewList = records.stream()
                .map(it -> {
                    KeyInfoDTO.KeyView view = new KeyInfoDTO.KeyView();
                    BeanUtils.copyProperties(it, view);
                    Optional.ofNullable(it.getApplicationId()).map(appNameMap::get).ifPresent(view::setAppName);
                    Optional.of(it.getKeyType()).map(KeyCategory::of).map(KeyCategory::getDesc).ifPresent(view::setKeyTypeText);
                    Map<String, String> usageMap = KeyUsage.getUsage(it.getKeyUsage())
                            .stream()
                            .collect(Collectors.toMap(KeyUsage::getCode, KeyUsage::getDesc));
                    view.setKeyUsages(new ArrayList<>(usageMap.keySet()));
                    view.setKeyUsageText(String.join(",", usageMap.values()));
                    KeyStatus keyStatus = KeyStatus.of(it.getStatus());
                    if (KeyStatus.ENABLED == keyStatus) {
                        if (now.isBefore(it.getEffectiveTime())) {
                            view.setStatus(KeyStatus.WAIT_ENABLED.getCode());
                            view.setStatusText(KeyStatus.WAIT_ENABLED.getDesc());
                        } else if (now.isAfter(it.getExpiredTime())) {
                            view.setStatus(KeyStatus.EXPIRED.getCode());
                            view.setStatusText(KeyStatus.EXPIRED.getDesc());
                        }
                    }
                    if (ObjectUtils.isEmpty(view.getStatusText())) {
                        view.setStatusText(keyStatus.getDesc());
                    }
                    return view;
                })
                .collect(Collectors.toList());

        return new Page<KeyInfoDTO.KeyView>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(viewList);
    }

    @Override
    public KeyInfoDTO.KeyView selectById(Long id) {
        KeyInfo keyInfo = keyInfoMapper.selectById(id);
        Assert.notNull(keyInfo, "密钥ID不存在");
        LocalDateTime now = LocalDateTime.now();
        KeyInfoDTO.KeyView view = new KeyInfoDTO.KeyView();
        BeanUtils.copyProperties(keyInfo, view);
        Optional.of(keyInfo.getKeyType()).map(KeyCategory::of).map(KeyCategory::getDesc).ifPresent(view::setKeyTypeText);
        Map<String, String> usageMap = KeyUsage.getUsage(keyInfo.getKeyUsage())
                .stream()
                .collect(Collectors.toMap(KeyUsage::getCode, KeyUsage::getDesc));
        view.setKeyUsages(new ArrayList<>(usageMap.keySet()));
        view.setKeyUsageText(String.join(",", usageMap.values()));
        KeyStatus keyStatus = KeyStatus.of(keyInfo.getStatus());
        if (KeyStatus.ENABLED == keyStatus) {
            if (now.isBefore(keyInfo.getEffectiveTime())) {
                view.setStatus(KeyStatus.WAIT_ENABLED.getCode());
                view.setStatusText(KeyStatus.WAIT_ENABLED.getDesc());
            } else if (now.isAfter(keyInfo.getExpiredTime())) {
                view.setStatus(KeyStatus.EXPIRED.getCode());
                view.setStatusText(KeyStatus.EXPIRED.getDesc());
            }
        }
        if (ObjectUtils.isEmpty(view.getStatusText())) {
            view.setStatusText(keyStatus.getDesc());
        }
        return view;
    }

    @Override
    public Long save(KeyInfoDTO.KeySave save) {

        KeyTemplate keyTemplate = keyTemplateMapper.selectOne(
                new LambdaQueryWrapper<KeyTemplate>()
                        .eq(KeyTemplate::getCode, save.getKeyTemplateCode())
        );
        Assert.notNull(keyTemplate, "密钥模版不存在");
        Application app = applicationMapper.selectById(save.getApplicationId());
        Assert.notNull(app, "所属应用不存在");
        Assert.isTrue(EnableStatus.ENABLED.getCode().equals(app.getStatus()), "应用不是启用状态");

        LocalDateTime now = LocalDateTime.now();

        for (int i = 0; i < save.getGenNumber(); i++) {

            // 密钥信息
            KeyInfo info = new KeyInfo();
            info.setId(IdWorker.getId());
            info.setApplicationId(save.getApplicationId());
            info.setKeyTemplateId(keyTemplate.getId());
            info.setCode(String.valueOf(info.getId()));
            info.setKeyType(keyTemplate.getKeyType());
            info.setKeyAlg(keyTemplate.getKeyAlg());
            info.setKeyLength(keyTemplate.getKeyLength());
            info.setKeyUsage(keyTemplate.getKeyUsage());
            info.setCheckAlg(keyTemplate.getCheckAlg());
            info.setCreateTime(LocalDateTime.now());

            info.setStatus(KeyStatus.ENABLED.getCode());

            ChronoUnit startUnit = ChronoUnit.valueOf(keyTemplate.getStartAfterCreateUnit().toUpperCase());
            info.setEffectiveTime(now.plusSeconds(startUnit.getDuration().getSeconds() * keyTemplate.getStartAfterCreateTime()));

            ChronoUnit validUnit = ChronoUnit.valueOf(keyTemplate.getValidUnit().toUpperCase());
            info.setExpiredTime(info.getEffectiveTime().plusSeconds(validUnit.getDuration().getSeconds() * keyTemplate.getValidTime()));

            // 具体使用的密钥值
            KeyRecord record = genKeyRecord(info);
            info.setCheckValue(record.getCheckValue());
            keyInfoMapper.insert(info);
            spKeyRecordMapper.insert(record);
        }

        return 0L;
    }

    @Override
    public void update(KeyInfoDTO.KeyUpdate update) {
        List<Long> ids = update.getIds();
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime newEffectTime = LocalDateTime.of(update.getEffectiveTime(), LocalTime.MIN);

        Assert.isTrue(!now.toLocalDate().isAfter(update.getEffectiveTime()), "生效日期必须在当前时间之后");

        List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(keyInfos)) {
            log.warn("updateKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
            return;
        }
        List<String> unNormalCodes = keyInfos.stream()
                .filter(it -> now.isBefore(it.getEffectiveTime())
                        || now.isAfter(it.getExpiredTime())
                        || newEffectTime.isAfter(it.getExpiredTime())
                        || !Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus())
                )
                .map(KeyInfo::getCode)
                .collect(Collectors.toList());
        Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
                "密钥id: " + String.join(",", unNormalCodes) + "不是启用状态或者新生效时间超过密钥过期时间, 无法更新");

        List<KeyRecord> records = spKeyRecordMapper.selectList(
                new LambdaQueryWrapper<KeyRecord>()
                        .gt(KeyRecord::getEffectiveTime, now)
                        .in(KeyRecord::getKeyId, ids)
        );
        String msgIds = records.stream().map(it -> String.valueOf(it.getKeyId())).collect(Collectors.joining(","));
        Assert.isTrue(CollectionUtils.isEmpty(records), "密钥id: " + msgIds + "已经存在未使用的新密钥, 无法更新");

        // 更新实际密钥值
        spKeyRecordMapper.update(
                new LambdaUpdateWrapper<KeyRecord>()
                        .set(KeyRecord::getExpiredTime, newEffectTime)
                        .in(KeyRecord::getKeyId, ids)
                        .lt(KeyRecord::getEffectiveTime, now)
                        .gt(KeyRecord::getExpiredTime, now)
        );

        for (KeyInfo info : keyInfos) {
            // 具体使用的密钥值
            KeyRecord record = genKeyRecord(info);
            record.setEffectiveTime(newEffectTime);

            KeyInfo upInfo = new KeyInfo();
            upInfo.setId(info.getId());
            upInfo.setCheckValue(record.getCheckValue());

            keyInfoMapper.updateById(upInfo);
            spKeyRecordMapper.insert(record);
        }

    }

    private KeyRecord genKeyRecord(KeyInfo info) {
        KeyRecord record = new KeyRecord();
        record.setId(IdWorker.getId());
        record.setKeyId(info.getId());
        record.setKeyIndex(String.valueOf(record.getId()));
        record.setCheckAlg(info.getCheckAlg());
        record.setEffectiveTime(info.getEffectiveTime());
        record.setExpiredTime(info.getExpiredTime());
        record.setCreateTime(LocalDateTime.now());

        if (KeyCategory.SYM_KEY.getCode().equals(info.getKeyType())) {
            byte[] symKey = sdfApiService.generateRandom(16);
            byte[] encSymKey = sdfApiService.encryptByTMK(symKey);
            record.setKeyData(Hex.toHexString(encSymKey));
            String checkHash = Hex.toHexString(sdfApiService.hash(symKey));
            record.setCheckValue(checkHash);
        } else {
            EccKey eccKey = sdfApiService.genKeyPairEcc();
            byte[] d = eccKey.getPriKey();
            byte[] encD = sdfApiService.encryptByTMK(d);
            record.setKeyData(Hex.toHexString(encD));
            String checkHash = Hex.toHexString(sdfApiService.hash(d));
            record.setCheckValue(checkHash);

            byte[] pubKeyBytes = eccKey.getPubKey();
            record.setPubKey(Hex.toHexString(pubKeyBytes));
            record.setPubIdx(record.getPubKey().substring(0, 8));
        }
        return record;
    }

    @Override
    public byte[] backupKey(KeyInfoDTO.Backup backup) {

        LocalDate plussed = backup.getStartTime().plusDays(100);
        Assert.isTrue(plussed.isAfter(backup.getEndTime()), "备份密钥创建时间范围不能超过100天");
        List<KeyInfo> keyInfos = keyInfoMapper.selectList(
                new LambdaQueryWrapper<KeyInfo>()
                        .eq(KeyInfo::getKeyType, backup.getKeyType())
                        .gt(KeyInfo::getCreateTime, LocalDateTime.of(backup.getStartTime(), LocalTime.MIN))
                        .lt(KeyInfo::getCreateTime, LocalDateTime.of(backup.getEndTime(), LocalTime.MAX))
        );
        Assert.isTrue(!CollectionUtils.isEmpty(keyInfos), "所选日期范围内,没有数据");
        StringBuilder builder = new StringBuilder();
        for (KeyInfo keyInfo : keyInfos) {
            List<KeyRecord> records = spKeyRecordMapper.selectList(
                    new LambdaQueryWrapper<KeyRecord>()
                            .eq(KeyRecord::getKeyId, keyInfo.getId())
            );
            keyInfo.setRecords(records);
            builder.append(Hex.toHexString(JsonUtils.toJsonBytes(keyInfo))).append(System.lineSeparator());
        }
        return builder.toString().getBytes(StandardCharsets.UTF_8);
    }

    @Override
    public String recoveryKey(InputStream is) {
        int suc = 0, count = 0, err = 0, exd = 0;

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
            String line;
            while (true) {
                try {
                    if ((line = reader.readLine()) == null) break;
                } catch (IOException e) {
                    throw new IllegalArgumentException("文件读取异常");
                }
                if (ObjectUtils.isEmpty(line)) {
                    continue;
                }
                count++;
                try {
                    KeyInfo info = JsonUtils.parse(Hex.decode(line), KeyInfo.class);
                    KeyInfo exist = keyInfoMapper.selectById(info.getId());
                    if (exist != null) {
                        exd++;
                        continue;
                    }
                    spKeyRecordMapper.delete(
                            new LambdaQueryWrapper<KeyRecord>()
                                    .eq(KeyRecord::getKeyId, info.getId())
                    );
                    keyInfoMapper.insert(info);
                    if (!CollectionUtils.isEmpty(info.getRecords())) {
                        info.getRecords().forEach(spKeyRecordMapper::insert);
                    }
                    suc++;
                } catch (Exception ex) {
                    log.error("解析恢复密钥失败, {}", line, ex);
                    err++;
                }
            }
        } catch (IOException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
        return String.format("恢复完成,共%d条数据,跳过已经存在的密钥%d条,恢复成功%d条,解析失败%d条", count, exd, suc, err);
    }

    @Override
    public String createCsr(KeyInfoDTO.CreateCsr createCsr) {

        String subject = createCsr.getDN();
        KeyCsr exist = keyCsrMapper.selectBySubject(subject);
        if (Objects.nonNull(exist)) {
            if (Objects.equals(exist.getKeyId(), createCsr.getId())) {
                return exist.getCsrTxt();
            }
            throw new IllegalArgumentException("此证书主题已经被使用!");
        }
        KeyInfo keyInfo = keyInfoMapper.selectById(createCsr.getId());
        Assert.notNull(keyInfo, "密钥不存在!");
        LocalDateTime now = LocalDateTime.now();
        Assert.isTrue(KeyCategory.ASYM_KEY.getCode().equals(keyInfo.getKeyType())
                        && KeyStatus.ENABLED.getCode().equals(keyInfo.getStatus())
                        && now.isAfter(keyInfo.getEffectiveTime())
                        && now.isBefore(keyInfo.getExpiredTime()),
                "只能选择已启用的非对称密钥生成证书请求");
        KeyRecord record = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
        Assert.notNull(record, "数据异常,没有在使用中的密钥");

        X500Name dn = new X500Name(subject);

        byte[] xy = Hex.decode(record.getPubKey());
        byte[] x = Arrays.copyOfRange(xy, 0, 32);
        byte[] y = Arrays.copyOfRange(xy, 32, 64);
        ECPublicKeyParameters pubKeyParam = BCECUtils.createECPublicKeyParameters(x, y);

        byte[] priKeyBytes = sdfApiService.decryptByTMK(Hex.decode(record.getKeyData()));
//        byte[][] pri18 = LangUtils.splitAverage(priKeyBytes);
        ECPrivateKeyParameters priKeyParam = BCECUtils.createECPrivateKeyParameters(priKeyBytes);
        ECDomainParameters domainParams = priKeyParam.getParameters();
        ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), domainParams.getN(), domainParams.getH());
        BCECPublicKey pubKey = new BCECPublicKey("EC", pubKeyParam, spec, BouncyCastleProvider.CONFIGURATION);
        BCECPrivateKey priKey = new BCECPrivateKey("EC", priKeyParam, pubKey, spec, BouncyCastleProvider.CONFIGURATION);
//
        String csrTxt;
        try {
            byte[] csr = CommonCertUtils.createCSR(dn, pubKey, priKey).getEncoded();

            PemObject pem = new PemObject("CERTIFICATE REQUEST", csr);
            StringWriter str = new StringWriter();
            PemWriter pemWriter = new PemWriter(str);
            pemWriter.writeObject(pem);
            pemWriter.close();
            str.close();

            csrTxt = str.toString();
        } catch (Exception e) {
            log.error("生成CSR异常", e);
            throw new IllegalArgumentException("生成证书请求异常");
        }
        KeyCsr csr = new KeyCsr();
        csr.setId(IdWorker.getId());
        csr.setApplicationId(keyInfo.getApplicationId());
        csr.setKeyId(keyInfo.getId());
        csr.setKeyRecordId(record.getId());
        csr.setPubKey(record.getPubKey());
        csr.setKeyData(record.getKeyData());
        csr.setSubject(subject);
        csr.setCreateTime(LocalDateTime.now());
        csr.setCsrTxt(csrTxt);
        keyCsrMapper.insert(csr);
        return csrTxt;
    }


    @Override
    public void enableKey(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(keyInfos)) {
            log.warn("enableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
            return;
        }
        List<String> unNormalCodes = keyInfos.stream()
                .filter(it -> !Objects.equals(KeyStatus.DISABLED.getCode(), it.getStatus()))
                .map(KeyInfo::getCode)
                .collect(Collectors.toList());
        Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
                "密钥id: " + String.join(",", unNormalCodes) + "当前状态不支持启用操作");
        keyInfoMapper.update(
                new LambdaUpdateWrapper<KeyInfo>()
                        .set(KeyInfo::getStatus, KeyStatus.ENABLED.getCode())
                        .in(KeyInfo::getId, ids)
        );
    }

    @Override
    public void disableKey(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(keyInfos)) {
            log.warn("disableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
            return;
        }
        List<String> unNormalCodes = keyInfos.stream()
                .filter(it -> !Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus()))
                .map(KeyInfo::getCode)
                .collect(Collectors.toList());
        Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
                "密钥id: " + String.join(",", unNormalCodes) + "当前状态不支持停用操作");
        keyInfoMapper.update(
                new LambdaUpdateWrapper<KeyInfo>()
                        .set(KeyInfo::getStatus, KeyStatus.DISABLED.getCode())
                        .in(KeyInfo::getId, ids)
        );
    }

    @Override
    public void archiveKey(List<Long> ids) {

        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(keyInfos)) {
            log.warn("archiveKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        List<String> unNormalCodes = keyInfos.stream()
                .filter(it -> !(Objects.equals(KeyStatus.DISABLED.getCode(), it.getStatus())
                        || (Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus()) && it.getExpiredTime().isBefore(now)))
                )
                .map(KeyInfo::getCode)
                .collect(Collectors.toList());
        Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
                "密钥id: " + String.join(",", unNormalCodes) + "当前状态不支持归档操作");
        keyInfoMapper.update(
                new LambdaUpdateWrapper<KeyInfo>()
                        .set(KeyInfo::getStatus, KeyStatus.ARCHIVED.getCode())
                        .in(KeyInfo::getId, ids)
        );
    }

    @Override
    public void destroyKey(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(keyInfos)) {
            log.warn("destroyKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        List<String> unNormalCodes = keyInfos.stream()
                .filter(it -> Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus())
                        && now.isAfter(it.getEffectiveTime())
                        && now.isBefore(it.getExpiredTime())
                )
                .map(KeyInfo::getCode)
                .collect(Collectors.toList());
        Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
                "密钥id: " + String.join(",", unNormalCodes) + "当前状态不支持销毁操作");

        keyInfoMapper.deleteBatchIds(ids);
        spKeyRecordMapper.delete(new LambdaUpdateWrapper<KeyRecord>().in(KeyRecord::getKeyId, ids));

//        keyInfoMapper.update(
//                new LambdaUpdateWrapper<KeyInfo>()
//                        .set(KeyInfo::getStatus, KeyStatus.DESTROY.getCode())
//                        .set(KeyInfo::getDeleted, true)
//                        .set(KeyInfo::getDeleteTime, now)
//                        .in(KeyInfo::getId, ids)
//        );
    }
}
