package com.skr.mdm.api.license.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.skr.mdm.api.license.MdmLicenseVerify;
import com.skr.mdm.bean.vo.company.AuthorizationInfoVO;
import com.skr.mdm.dao.AuCompanyDao;
import com.skr.mdm.dao.DeviceEquipmentDao;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.AuCompanyDO;
import com.skr.mdm.entity.DeviceEquipmentDO;
import com.skr.mdm.enums.DeleteStateEnum;
import com.skr.mdm.enums.ResultCodeEnum;
import com.skr.mdm.license.*;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.SmUtils;
import de.schlichtherle.license.LicenseManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import java.io.File;
import java.util.Date;
import java.util.Objects;

/**
 * @author nfq
 * @date 2021/09/01 17:12
 */
@RestController
@RequestMapping("license")
@Api(tags = "license授权")
@Slf4j
public class LicenseController {

    @Value("${mdm.license.company}")
    private Boolean companyLicense;

    /**
     * 证书subject
     */
    @Value("${license.subject}")
    private String subject;
    /**
     * 公钥别称
     */
    @Value("${license.publicAlias}")
    private String publicAlias;
    /**
     * 访问公钥库的密码
     */
    @Value("${license.storePass}")
    private String storePass;

    @Autowired
    private DeviceEquipmentDao equipmentDao;
    @Autowired
    private AuCompanyDao companyDao;

    @PostMapping(value = "info")
    @ApiOperation(value = "证书信息")
    public ResponseResult getServerInfos(@ApiIgnore Authentication authentication) throws Exception {
        LoginAdminDTO loginAdminDTO = (LoginAdminDTO) authentication.getPrincipal();
        String osName = System.getProperty("os.name").toLowerCase();
        AbstractServerInfo abstractServerInfos = null;
        if (osName.startsWith("windows")) {
            abstractServerInfos = new WindowsServerInfo();
        } else if (osName.startsWith("linux")) {
            abstractServerInfos = new LinuxServerInfo();
        } else {
            abstractServerInfos = new LinuxServerInfo();
        }
        AuthorizationInfoVO authorizationInfoVO = new AuthorizationInfoVO();
        authorizationInfoVO.setMachineCode(abstractServerInfos.getServerInfos().getMachineCode());
        if (companyLicense) {
            AuCompanyDO companyDO = companyDao.selectById(loginAdminDTO.getCompanyId());
            String allowUseNumber = companyDO.getAllowUseNumber();
            SM2 sm2 = SmUtils.sm2();
            String expirationTime = StrUtil.utf8Str(sm2.decryptFromBcd(companyDO.getExpirationTime(), KeyType.PrivateKey));
            authorizationInfoVO.setAllowUseNumber(StrUtil.utf8Str(sm2.decryptFromBcd(allowUseNumber, KeyType.PrivateKey)));
            authorizationInfoVO.setExpirationTime(new Date(Long.parseLong(expirationTime)));
        } else {
            LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
            if (licenseManager != null) {
                CustomLicenseManager customLicenseManager = (CustomLicenseManager) licenseManager;
                authorizationInfoVO.setAllowUseNumber(customLicenseManager.getAllowUseNumber());
                authorizationInfoVO.setExpirationTime(customLicenseManager.getExpirationTime());
            }
        }
        return ResponseResult.success(authorizationInfoVO);
    }


    @PostMapping(value = "install")
    @ApiOperation(value = "安装证书")
    public ResponseResult installLicense(MultipartFile file, @ApiIgnore Authentication authentication) throws Exception {
        if (companyLicense) {
            return ResponseResult.failure("测试环境不支持文件配置，请直接修改所属公司授权参数！");
        }
        LoginAdminDTO loginAdminDTO = (LoginAdminDTO) authentication.getPrincipal();
        String licensePath = MdmLicenseVerify.getLicensePath();
        String transitionLicensePath = null;
        File licensePathFile = new File(licensePath);
        File transitionFile = null;
        if (!licensePathFile.exists()) {
            licensePathFile.createNewFile();
            file.transferTo(licensePathFile);
        } else {
            transitionLicensePath = MdmLicenseVerify.getTransitionLicensePath();
            transitionFile = new File(transitionLicensePath);
            transitionFile.createNewFile();
            file.transferTo(transitionFile);
        }

        LicenseVerifyParam param = new LicenseVerifyParam();
        param.setLicensePath(licensePath);
        if (StringUtils.isNotBlank(transitionLicensePath)) {
            param.setLicensePath(transitionLicensePath);
        }
        param.setSubject(subject);
        param.setPublicAlias(publicAlias);
        param.setStorePass(storePass);
        param.setPublicKeysStorePath(MdmLicenseVerify.getLicensePublicCertPath());

        LicenseVerify licenseVerify = new LicenseVerify();
        // 安装证书
        licenseVerify.install(param);
        Integer allowUseNumber = MdmLicenseVerify.getAllowUseNumber();
        Long equipmentCount = new LambdaQueryChainWrapper<>(equipmentDao)
                .eq(DeviceEquipmentDO::getCompanyId, loginAdminDTO.getCompanyId())
                .eq(DeviceEquipmentDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .count();
        if (equipmentCount > allowUseNumber) {
            // 卸载新证书
            LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
            licenseManager.uninstall();
            if (transitionFile.exists()) {
                transitionFile.delete();
            }
            // 重新安装旧证书
            param.setLicensePath(licensePath);
            licenseVerify.install(param);
            return ResponseResult.failure(ResultCodeEnum.ALLOW_NUMBER_ERROR);
        }
        // 证书校验成功，临时证书替换为正式证书，并删除临时证书
        if (StringUtils.isNotBlank(transitionLicensePath)) {
            FileUtils.copyFile(transitionFile, licensePathFile);
            if (transitionFile.exists()) {
                transitionFile.delete();
            }
        }
        // 更新数据库授权数量
        SM2 sm2 = SmUtils.sm2();
        String authorizationNumber = sm2.encryptBcd(allowUseNumber.toString(), KeyType.PublicKey);
        String expireTime = sm2.encryptBcd(String.valueOf(Objects.requireNonNull(MdmLicenseVerify.getExpireTime()).getTime()), KeyType.PublicKey);
        new LambdaUpdateChainWrapper<>(companyDao)
                .set(AuCompanyDO::getAllowUseNumber, authorizationNumber)
                .set(AuCompanyDO::getExpirationTime, expireTime)
                .eq(AuCompanyDO::getId, loginAdminDTO.getCompanyId())
                .update();
        return ResponseResult.success();
    }

}
