package cn.gintone.service;

import cn.gintone.ErrorInfo;
import cn.gintone.config.IotDbConfig;
import cn.gintone.controller.vo.ImportantFileSaveReqVO;
import cn.gintone.controller.vo.KeyCodePageReqVO;
import cn.gintone.controller.vo.KeyCodeSaveReqVO;
import cn.gintone.dal.KeyCodeMapper;
import cn.gintone.dto.EncInfo;
import cn.gintone.dto.FileDecLogInfo;
import cn.gintone.dto.JarDecLogInfo;
import cn.gintone.dto.SpePeoLogInfo;
import cn.gintone.encryptionUtils.*;
import cn.gintone.entity.KeyCodeDO;
import cn.gintone.iotdbUtils.FileIotDbUtil;
import cn.gintone.iotdbUtils.JarDecLogIotDbUtil;
import cn.gintone.iotdbUtils.SpecialPeopleIotDbUtils;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.auth.AdminAuthService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;


import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 公钥私钥管理 Service 实现类
 *
 * @author 胡懿
 */
@Service
@Validated
public class KeyCodeServiceImpl implements KeyCodeService {
    @Autowired
    private IotDbConfig iotDbConfig;

    @Resource
    private KeyCodeMapper keyCodeMapper;
    @Resource
    private AdminAuthService authService;

    @Override
    public Long createKeyCode(KeyCodeSaveReqVO createReqVO) {
        // 插入
        KeyCodeDO keyCode = BeanUtils.toBean(createReqVO, KeyCodeDO.class);
        keyCodeMapper.insert(keyCode);
        // 返回
        return keyCode.getId();
    }

    @Override
    public void updateKeyCode(KeyCodeSaveReqVO updateReqVO) {
        // 校验存在
        validateKeyCodeExists(updateReqVO.getId());
        // 更新
        KeyCodeDO updateObj = BeanUtils.toBean(updateReqVO, KeyCodeDO.class);
        keyCodeMapper.updateById(updateObj);
    }

    @Override
    public void deleteKeyCode(Long id) {
        // 校验存在
        validateKeyCodeExists(id);
        // 删除
        keyCodeMapper.deleteById(id);
    }

    private void validateKeyCodeExists(Long id) {
        if (keyCodeMapper.selectById(id) == null) {
            throw exception(ErrorInfo.KEY_CODE_NOT_EXISTS);
        }
    }

    @Override
    public KeyCodeDO getKeyCode(Long id) {
        return keyCodeMapper.selectById(id);
    }

    @Override
    public PageResult<KeyCodeDO> getKeyCodePage(KeyCodePageReqVO pageReqVO) {
        return keyCodeMapper.selectPage(pageReqVO);
    }

    @Override
    public String initKey() {
        List<KeyCodeDO> keyCodeDOS = keyCodeMapper.selectList(new QueryWrapper<KeyCodeDO>());
        System.out.println("keyCodeDOS:" + keyCodeDOS.size());
        if (null != keyCodeDOS && keyCodeDOS.size() > 0) {
            for (KeyCodeDO keyCodeDO : keyCodeDOS){
                keyCodeMapper.deleteById(keyCodeDO.getId());
            }
        }

        try {
            KeyCodeDO keyCodeDO_rsa = new KeyCodeDO();
            Map<String, String> keyMap = PemFileGenerator.generateAndSaveKeyPair(1024);
            String pubKey_rsa = keyMap.get("publicPem");
            String priKey_rsa = keyMap.get("privatePem");
            keyCodeDO_rsa.setType(1);
            keyCodeDO_rsa.setPublicKey(pubKey_rsa);
            keyCodeDO_rsa.setPrivateKey(priKey_rsa);
            int insert = keyCodeMapper.insert(keyCodeDO_rsa);


            KeyCodeDO keyCodeDo_sm2 = new KeyCodeDO();
            KeyPair keyPair = SM2Util.generateSm2KeyPair();
            PublicKey publicKey = keyPair.getPublic();
            PrivateKey privateKey = keyPair.getPrivate();

            String pubStr_sm2 = SM2KeyUtils.publicKeyToString(publicKey);
            String priStr_sm2 = SM2KeyUtils.privateKeyToString(privateKey);
            keyCodeDo_sm2.setType(2);
            keyCodeDo_sm2.setPublicKey(pubStr_sm2);
            keyCodeDo_sm2.setPrivateKey(priStr_sm2);
            keyCodeMapper.insert(keyCodeDo_sm2);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "初始化成功";
    }

    @Override
    public EncInfo rasEncryption(Map<String, Object> requestMap) {
        EncInfo encInfo = new EncInfo();
        KeyCodeDO keyCodeDO = keyCodeMapper.selectOne(new QueryWrapper<KeyCodeDO>().eq("type", 1));
        if (null != keyCodeDO) {
            Map<String, Object> resultMap = new HashMap<>();
            Set<Map.Entry<String, Object>> entries = requestMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                try {
                    encInfo.setPrivateKey(keyCodeDO.getPrivateKey());
                    PublicKey publicKey = SecureHybridDecryptor.loadPublicKey(keyCodeDO.getPublicKey());
                    String encrypt = SecureHybridEncryptor.encrypt(value, publicKey);
                    resultMap.put(key, encrypt);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            encInfo.setInfo(resultMap);
        }
        return encInfo;
    }

    @Override
    public Map<String, Object> rasDecrypt(EncInfo encInfo, String clientIp, String pdToken, boolean isWai) {
        try {
            PrivateKey pteKey = null;
            if (!isWai) {
                pteKey = SecureHybridDecryptor.loadPrivateKey(encInfo.getPrivateKey());
            } else {
                pteKey = SecureHybridDecryptor.loadPrivateKey(encInfo.getPrivateKey());
            }
            // 解密
            Map<String, Object> resultMap = new HashMap<>();
            Map<String, Object> infoMap = encInfo.getInfo();
            Set<Map.Entry<String, Object>> entries = infoMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                String info = entry.getValue().toString();
                String decrypted = SecureHybridDecryptor.decrypt(info, pteKey);
                resultMap.put(key, decrypted);
            }

            if (isWai) {
                AdminUserDO user = authService.getPdUserByToken(pdToken);
                JarDecLogInfo jarDecLogInfo = new JarDecLogInfo();
                jarDecLogInfo.setClientIp(clientIp);
                jarDecLogInfo.setSysAbbre(encInfo.getSysAbbre());
                jarDecLogInfo.setContent(JSON.toJSONString(infoMap));
                jarDecLogInfo.setPrivateKey(encInfo.getPrivateKey());
                jarDecLogInfo.setUserId(user.getId() + "");
                jarDecLogInfo.setUsername(user.getUsername());
                jarDecLogInfo.setType("ras");
                JarDecLogIotDbUtil.inserOne(iotDbConfig, jarDecLogInfo);
            }


            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public EncInfo smTwoEncryption(Map<String, Object> requestMap) {
        EncInfo encInfo = new EncInfo();
        KeyCodeDO keyCodeDO = keyCodeMapper.selectOne(new QueryWrapper<KeyCodeDO>().eq("type", 2));
        if (null != keyCodeDO) {
            Map<String, Object> resultMap = new HashMap<>();
            Set<Map.Entry<String, Object>> entries = requestMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                try {
                    encInfo.setPrivateKey(keyCodeDO.getPrivateKey());
                    PublicKey publicKey = SM2KeyUtils.stringToPublicKey(keyCodeDO.getPublicKey());
                    byte[] data = value.getBytes("UTF-8");
                    // 加密
                    byte[] encryptedData = SM2Util.sm2Encrypt(data, publicKey);
                    String encrypt = Base64.getEncoder().encodeToString(encryptedData);
                    resultMap.put(key, encrypt);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            encInfo.setInfo(resultMap);
        }
        return encInfo;
    }

    @Override
    public Map<String, Object> smTwoDecrypt(EncInfo encInfo, String clientIp, String pdToken) {
        try {
            PrivateKey privateKey = SM2KeyUtils.stringToPrivateKey(encInfo.getPrivateKey());
            // 解密
            Map<String, Object> resultMap = new HashMap<>();
            Map<String, Object> infoMap = encInfo.getInfo();
            Set<Map.Entry<String, Object>> entries = infoMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                String key = entry.getKey();
                String info = entry.getValue().toString();
                byte[] decodedBytes = Base64.getDecoder().decode(info);
                byte[] decryptedData = SM2Util.sm2Decrypt(decodedBytes, privateKey);
                String decrypted = new String(decryptedData, "UTF-8");
                resultMap.put(key, decrypted);
            }

            AdminUserDO user = authService.getPdUserByToken(pdToken);
            SpePeoLogInfo spePeoLogInfo = new SpePeoLogInfo();
            spePeoLogInfo.setClientIp(clientIp);
            spePeoLogInfo.setSysAbbre(encInfo.getSysAbbre());
            spePeoLogInfo.setContent(JSON.toJSONString(infoMap));
            spePeoLogInfo.setPrivateKey(encInfo.getPrivateKey());
            spePeoLogInfo.setUserId(user.getId() + "");
            spePeoLogInfo.setUsername(user.getUsername());
            SpecialPeopleIotDbUtils.inserOne(iotDbConfig, spePeoLogInfo);
            return resultMap;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Map<String, Object> fileRasDecrypt(ImportantFileSaveReqVO saveReqVO, String clientIp, String pdToken) {
        EncInfo encInfo = new EncInfo();
        Map<String, Object> map = new HashMap<>();
        map.put("url", saveReqVO.getUrl());
        encInfo.setInfo(map);
        encInfo.setPrivateKey(saveReqVO.getPrivateKey());
        Map<String, Object> resultMap = rasDecrypt(encInfo, clientIp, pdToken, false);

        AdminUserDO user = authService.getPdUserByToken(pdToken);
        FileDecLogInfo fileDecLogInfo = new FileDecLogInfo();
        fileDecLogInfo.setClientIp(clientIp);
        fileDecLogInfo.setSysAbbre("sec");
        fileDecLogInfo.setContent(saveReqVO.getUrl());
        fileDecLogInfo.setPrivateKey(saveReqVO.getPrivateKey());
        fileDecLogInfo.setUserId(user.getId() + "");
        fileDecLogInfo.setUsername(user.getUsername());
        fileDecLogInfo.setFileId(saveReqVO.getId());
        fileDecLogInfo.setFileName(saveReqVO.getName());
        FileIotDbUtil.inserOne(iotDbConfig, fileDecLogInfo);
        return resultMap;
    }

    @Override
    public KeyCodeDO getByType(Integer type) {
        KeyCodeDO keyCodeDO = keyCodeMapper.selectOne(new QueryWrapper<KeyCodeDO>().lambda().eq(KeyCodeDO::getType, type));
        return keyCodeDO;
    }

}