package com.skr.mdm.api.authority.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.skr.mdm.api.authority.service.MdmAuthorizationService;
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.LogAdminTypeEnum;
import com.skr.mdm.enums.ResultCodeEnum;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

/**
 * @author nfq
 * @date 2021/1/20 18:04
 */
@Service
@Slf4j
public class AuthorizationServiceImpl implements MdmAuthorizationService {

    @Value("${request.secret}")
    private String secret;

    private static final String AUTHORIZATION_NUMBER = "authorization.number";
    private static final String AUTHORIZATION_EXPIRE_TIME = "authorization.expire.time";
    // 机器码
    private static final String AUTHORIZATION_MACHINE_CODE = "authorization.machine.code";
    // 签名
    private static final String AUTHORIZATION_SIGN = "authorization.sign";

    private static final String EXT_NAME = "properties";

    private static final String AUTHORIZATION_PROPERTIES = "authorization.properties";

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


    @Override
    public ResponseResult config(MultipartFile file, LoginAdminDTO loginAdminDTO) throws IOException {
        if (file.isEmpty()) {
            return ResponseResult.failure();
        }
        String filename = file.getOriginalFilename();
        String extName = filename.substring(filename.lastIndexOf(".") + 1);
        if (!EXT_NAME.equals(extName)) {
            return ResponseResult.failure(ResultCodeEnum.FILE_TYPE_MATCHING);
        }

        Long equipmentCount = new LambdaQueryChainWrapper<>(equipmentDao)
                .eq(DeviceEquipmentDO::getCompanyId, loginAdminDTO.getCompanyId())
                .eq(DeviceEquipmentDO::getIsDelete, DeleteStateEnum.NON_DELETE.getState())
                .count();

        ResourceBundle resource = new PropertyResourceBundle(file.getInputStream());
        Enumeration<String> keys = resource.getKeys();
        AuCompanyDO companyDO = new AuCompanyDO();
        companyDO.setId(loginAdminDTO.getCompanyId());
        String decryptAllowNumber = "";
        String decryptExpirationTime = "";
        String decryptMachineCode = "";
        String sign = "";
        SM2 sm2 = SmUtils.sm2();
        RSA rsa = RSAUtil.rsa();
        while (keys.hasMoreElements()) {
            String element = keys.nextElement();
            // 授权数量 到期时间加密
            if (AUTHORIZATION_MACHINE_CODE.equals(element)) {
                String machineCode = resource.getString(element);
                decryptMachineCode = rsa.decryptStr(machineCode, KeyType.PrivateKey);
                if (!decryptMachineCode.equals(getMachineCode())) {
                    return ResponseResult.failure("授权文件认证失败");
                }
            }
            if (AUTHORIZATION_NUMBER.equals(element)) {
                String authorizationNumber = resource.getString(element);
                decryptAllowNumber = rsa.decryptStr(authorizationNumber, KeyType.PrivateKey);
                if (equipmentCount > Integer.parseInt(decryptAllowNumber)) {
                    return ResponseResult.failure(ResultCodeEnum.ALLOW_NUMBER_ERROR);
                }
                authorizationNumber = sm2.encryptBcd(decryptAllowNumber, KeyType.PublicKey);
                companyDO.setAllowUseNumber(authorizationNumber);
            }
            if (AUTHORIZATION_EXPIRE_TIME.equals(element)) {
                String authorizationExpireTime = resource.getString(element);
                decryptExpirationTime = rsa.decryptStr(authorizationExpireTime, KeyType.PrivateKey);
                authorizationExpireTime = sm2.encryptBcd(decryptExpirationTime, KeyType.PublicKey);
                companyDO.setExpirationTime(authorizationExpireTime);
            }
            if (AUTHORIZATION_SIGN.equals(element)) {
                sign = resource.getString(element);
            }
        }
        String data = decryptMachineCode + decryptAllowNumber + decryptExpirationTime + secret;
        if (!DigestUtil.md5Hex(data).equals(sign)) {
            return ResponseResult.failure("授权文件认证失败");
        }
        companyDao.updateById(companyDO);
        String content = "授权数量:" + decryptAllowNumber + ";到期日期：" +
                DateUtil.format(DateUtil.date(Long.parseLong(decryptExpirationTime)), "yyyy-MM-dd HH:mm:ss");
        String module = "授权管理";
        logAdminHandler.log(loginAdminDTO, "", LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, module);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult info(Integer companyId) {
        AuCompanyDO companyDO = companyDao.selectById(companyId);
        AuthorizationInfoVO authorizationInfoVO = new AuthorizationInfoVO();
        if (companyDO != null) {
            SM2 sm2 = SmUtils.sm2();
            if (StringUtils.isNotBlank(companyDO.getAllowUseNumber())) {
                String allowUseNumber = StrUtil.utf8Str(sm2.decryptFromBcd(companyDO.getAllowUseNumber(), KeyType.PrivateKey));
                authorizationInfoVO.setAllowUseNumber(allowUseNumber);
            }
            if (StringUtils.isNotBlank(companyDO.getExpirationTime())) {
                String expirationTime = StrUtil.utf8Str(sm2.decryptFromBcd(companyDO.getExpirationTime(), KeyType.PrivateKey));
                authorizationInfoVO.setExpirationTime(new Date(Long.parseLong(expirationTime)));
            }
            authorizationInfoVO.setMachineCode(getMachineCode());
        }
        return ResponseResult.success(authorizationInfoVO);
    }

    @Override
    public ResponseResult encrypt(Long data) {
        RSA rsa = RSAUtil.rsa();
        String encryptBcd = rsa.encryptBcd(String.valueOf(data), KeyType.PublicKey);
        return ResponseResult.success(encryptBcd);
    }

    @Override
    public void createEncryptFile(Integer authorizationNumber, Long authorizationExpireTime, String machineCode, HttpServletResponse response) throws Exception {
        RSA rsa = RSAUtil.rsa();
        StringBuilder stringBuilder = new StringBuilder();
        String text = "";
        // 机器码
        String machineCodeEncrypt = rsa.encryptBcd(machineCode, KeyType.PublicKey);
        text = stringBuilder
                .append(AUTHORIZATION_MACHINE_CODE)
                .append("=")
                .append(machineCodeEncrypt)
                .append("\n")
                .toString();
        // 授权数量
        if (authorizationNumber != null) {
            String authorizationNumberEncrypt = rsa.encryptBcd(String.valueOf(authorizationNumber), KeyType.PublicKey);
            text = stringBuilder
                    .append(AUTHORIZATION_NUMBER)
                    .append("=")
                    .append(authorizationNumberEncrypt)
                    .append("\n")
                    .toString();
        }
        // 到期时间
        if (authorizationExpireTime != null) {
            String authorizationExpireTimeEncrypt = rsa.encryptBcd(String.valueOf(authorizationExpireTime), KeyType.PublicKey);
            text = stringBuilder
                    .append(AUTHORIZATION_EXPIRE_TIME)
                    .append("=")
                    .append(authorizationExpireTimeEncrypt)
                    .append("\n")
                    .toString();
        }
        // 签名
        String sign = machineCode + authorizationNumber + authorizationExpireTime + secret;
        text = stringBuilder
                .append(AUTHORIZATION_SIGN)
                .append("=")
                .append(DigestUtil.md5Hex(sign))
                .toString();
        String path = ServerSystemUtil.userDir();
        if (ServerSystemUtil.osName().contains("win")) {
            path += "\\";
        } else {
            path += "/";
        }
        path = path + AUTHORIZATION_PROPERTIES;
        File file = new File(path);
        FileWriter writer = new FileWriter(file);
        writer.write(text);

        FileDownloadUtil.download(response, file, AUTHORIZATION_PROPERTIES);

        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 获取机器码
     *
     * @return
     */
    private String getMachineCode() {
        String machineCode = "";
        if (ServerSystemUtil.osName().contains("win")) {
            machineCode = DigestUtil.md5Hex(WindowsServerUtil.getCPUSerial() + WindowsServerUtil.getMainBoardSerial());
        } else {
            machineCode = DigestUtil.md5Hex(LinuxServerUtil.getCPUSerial() + LinuxServerUtil.getMainBoardSerial());
        }
        return machineCode;
    }

}
