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.mapper.CaCertMapper;
import com.sunyard.chsm.model.dto.CertDTO;
import com.sunyard.chsm.model.entity.CaCert;
import com.sunyard.chsm.service.CaCertService;
import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
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.security.PublicKey;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author liulu
 * @since 2024/11/11
 */
@Slf4j
@Service
@Transactional
public class CaCertServiceImpl implements CaCertService {

    @Resource
    private CaCertMapper caCertMapper;


    @Override
    public Page<CertDTO.CAView> selectPageList(CertDTO.CAQuery query) {
        IPage<CaCert> page = caCertMapper.selectPage(
                new Page<>(query.getPageNumber(), query.getPageSize()),
                new LambdaQueryWrapper<CaCert>()
                        .like(StringUtils.hasText(query.getCaName()), CaCert::getCaName, query.getCaName())
                        .orderByDesc(CaCert::getCreateTime)
        );
        List<CaCert> records = page.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        }

        List<CertDTO.CAView> viewList = records.stream()
                .map(it -> {
                    CertDTO.CAView view = new CertDTO.CAView();
                    BeanUtils.copyProperties(it, view);
                    return view;
                })
                .collect(Collectors.toList());

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

    @Override
    public Long save(CertDTO.CaInfo caInfo) {
        checkName(caInfo.getCaName());

        X509Certificate x509Cert;
        try {
            x509Cert = BCSM2CertUtils.getX509Cert(caInfo.getCertText());
        } catch (Exception ex) {
            throw new IllegalArgumentException("证书内容格式错误,无法解析");
        }

        CaCert cert = genCaCert(caInfo, x509Cert);
        cert.setId(IdWorker.getId());
        cert.setCreateTime(LocalDateTime.now());
        caCertMapper.insert(cert);

        return cert.getId();
    }

    @Override
    public void update(CertDTO.CaInfo update) {
        Assert.notNull(update.getId(), "id不能为空");
        CaCert exist = caCertMapper.selectById(update.getId());
        Assert.notNull(exist, "id对应的CA不存在");

        if (!Objects.equals(exist.getCaName(), update.getCaName())) {
            checkName(update.getCaName());
        }

        X509Certificate x509Cert;
        try {
            x509Cert = BCSM2CertUtils.getX509Cert(update.getCertText());
        } catch (Exception ex) {
            throw new IllegalArgumentException("证书内容格式错误,无法解析");
        }

        CaCert cert = genCaCert(update, x509Cert);
        cert.setId(update.getId());
        cert.setUpdateTime(LocalDateTime.now());
        caCertMapper.updateById(cert);
    }


    private static CaCert genCaCert(CertDTO.CaInfo caInfo, X509Certificate x509Cert) {

        CaCert cert = new CaCert();
        cert.setKeyAlg(caInfo.getKeyAlg());
        cert.setCaName(caInfo.getCaName());
        cert.setCaUrl(caInfo.getCaUrl());
        cert.setCertText(caInfo.getCertText());
        cert.setRemark(caInfo.getRemark());

        cert.setSubject(x509Cert.getSubjectX500Principal().getName());
        cert.setSerialNumber(x509Cert.getSerialNumber().toString());
        cert.setIssuerDn(x509Cert.getIssuerX500Principal().getName());
        cert.setNotBefore(x509Cert.getNotBefore());
        cert.setNotAfter(x509Cert.getNotAfter());

        PublicKey publicKey = x509Cert.getPublicKey();
        String hexPubKey = BCECUtils.getHexPubKey((BCECPublicKey) publicKey);
        cert.setPubKey(hexPubKey);
        return cert;
    }

    private void checkName(String name) {

        CaCert exist = caCertMapper.selectOne(
                new LambdaQueryWrapper<CaCert>().eq(CaCert::getCaName, name)
        );
        Assert.isNull(exist, "CA名称已经存在");
    }

    @Override
    public void delete(Long id) {
        caCertMapper.deleteById(id);
    }
}
