package com.sunyard.chsm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.dto.KeyTemplateDTO;
import com.sunyard.chsm.enums.KeyAlg;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.mapper.KeyTemplateMapper;
import com.sunyard.chsm.model.entity.KeyTemplate;
import com.sunyard.chsm.service.KeyTemplateService;
import lombok.extern.slf4j.Slf4j;
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.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
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/22
 */
@Slf4j
@Service
@Transactional
public class KeyTemplateServiceImpl implements KeyTemplateService {

    @Resource
    private KeyTemplateMapper keyTemplateMapper;


    @Override
    public Page<KeyTemplateDTO.KTView> selectPageList(KeyTemplateDTO.Query query) {

        IPage<KeyTemplate> page = keyTemplateMapper.selectPage(
                new Page<>(query.getPageNumber(), query.getPageSize()),
                new LambdaQueryWrapper<KeyTemplate>()
                        .eq(StringUtils.hasText(query.getKeyType()), KeyTemplate::getKeyType, query.getKeyType())
                        .orderByDesc(KeyTemplate::getCreateTime)
        );
        List<KeyTemplate> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        }
        List<KeyTemplateDTO.KTView> viewList = records.stream()
                .map(it -> {
                    KeyTemplateDTO.KTView view = new KeyTemplateDTO.KTView();
                    BeanUtils.copyProperties(it, view);
                    Optional.ofNullable(KeyCategory.of(it.getKeyType()))
                            .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()));
                    return view;
                })
                .collect(Collectors.toList());

        return new Page<KeyTemplateDTO.KTView>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(viewList);

    }

    @Override
    public Long save(KeyTemplateDTO.KTSave save) {

        checkEnum(save);
        checkCode(save.getCode());

        KeyTemplate add = mapToEntity(save);
        add.setId(IdWorker.getId());
        add.setCreateTime(LocalDateTime.now());

        keyTemplateMapper.insert(add);

        return add.getId();
    }

    @Override
    public void update(KeyTemplateDTO.KTSave update) {
        Assert.notNull(update.getId(), "id不能为空");

        checkEnum(update);

        KeyTemplate exist = keyTemplateMapper.selectById(update.getId());
        Assert.notNull(exist, "要更新的密钥模版不存在");

        if (!Objects.equals(exist.getCode(), update.getCode())) {
            checkCode(update.getCode());
        }

        KeyTemplate up = mapToEntity(update);
        up.setId(update.getId());
        up.setUpdateTime(LocalDateTime.now());

        keyTemplateMapper.updateById(up);
    }

    @Override
    public void delete(Long id) {
        Assert.notNull(id, "id不能为空");
        KeyTemplate exist = keyTemplateMapper.selectById(id);
        Assert.notNull(exist, "要更新的密钥模版不存在");
        keyTemplateMapper.deleteById(id);
    }

    private KeyTemplate mapToEntity(KeyTemplateDTO.KTSave save) {
        KeyTemplate entity = new KeyTemplate();
        entity.setCode(save.getCode());
        entity.setName(save.getName());
        entity.setKeyType(save.getKeyType());
        entity.setKeyAlg(save.getKeyAlg());
        entity.setKeyLength(save.getKeyLength());
        entity.setKeyUsage(KeyUsage.getStatus(save.getKeyUsages()));
        entity.setCheckAlg("SM3");
        entity.setValidTime(save.getValidTime());
        entity.setValidUnit(save.getValidUnit());
        entity.setStartAfterCreateTime(save.getStartAfterCreateTime());
        entity.setStartAfterCreateUnit(save.getStartAfterCreateUnit());
        entity.setRemark(save.getRemark());
        return entity;
    }

    private void checkEnum(KeyTemplateDTO.KTSave save) {
        KeyCategory keyCategory = KeyCategory.of(save.getKeyType());
        Assert.notNull(keyCategory, "不支持的密钥类型: " + save.getKeyType());
        KeyAlg keyAlg = KeyAlg.of(save.getKeyAlg());
        Assert.notNull(keyAlg, "不支持的密钥算法: " + save.getKeyAlg());
        Assert.isTrue(keyAlg.getCategory() == keyCategory, "密钥类型和密钥算法不匹配");

        List<KeyUsage> usageList = save.getKeyUsages().stream()
                .map(String::toUpperCase)
                .map(KeyUsage::valueOf)
                .collect(Collectors.toList());

        if (KeyCategory.SYM_KEY == keyCategory) {
            Assert.isTrue(!usageList.contains(KeyUsage.SIGN_VERIFY), "对称密钥不能用于签名验签");
        } else {
            Assert.isTrue(!usageList.contains(KeyUsage.HMAC), "非对称密钥不能用于计算HMac");
            Assert.isTrue(!usageList.contains(KeyUsage.MAC), "非对称密钥不能用于计算MAC");
        }

        ChronoUnit validUnit = ChronoUnit.valueOf(save.getValidUnit().toUpperCase());
        Assert.isTrue(validUnit == ChronoUnit.YEARS || validUnit == ChronoUnit.MONTHS
                || validUnit == ChronoUnit.DAYS, "密钥有效期时间单位仅支持年月日");
        ChronoUnit startUnit = ChronoUnit.valueOf(save.getStartAfterCreateUnit().toUpperCase());
        Assert.isTrue(startUnit == ChronoUnit.MONTHS || startUnit == ChronoUnit.DAYS
                || startUnit == ChronoUnit.HOURS, "延迟生效时间单位仅支持月日小时");
    }

    private void checkCode(String code) {
        LambdaQueryWrapper<KeyTemplate> wrapper = new LambdaQueryWrapper<KeyTemplate>()
                .eq(KeyTemplate::getCode, code);
        KeyTemplate exist = keyTemplateMapper.selectOne(wrapper);
        Assert.isNull(exist, "模版编号已存在");
    }
}
