package com.jianshui.breaker.jianshuiy;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import com.alibaba.fastjson.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.security.SecureRandom;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 生成简税授权文件
 * @Date 2022/5/28 15:45
 **/
public class Breaker {

    public static void main(String[] args) throws IOException {
        // 解密
        Map<String, String> config = readFile("/Users/gongquanlin/Library/Containers/com.tencent.xinWeChat/Data/Library/Application Support/com.tencent.xinWeChat/2.0b4.0.9/c64e7f36ab6e2f78465992785c679e3d/Message/MessageTemp/2c5ab899f09d4012f4f34b5da15a4ac0/File/BZ91370112MA3WCCA2XC.Render", "370103999999061");
        String utf8 = CharsetUtil.convert(JSONObject.toJSONString(config), CharsetUtil.GBK, CharsetUtil.UTF_8);
        System.out.println("config is " + utf8);

        // 下面所有的BZ，都是ys"\tomcat\webapps\FPGLXT\WEB-INF\classes\config.properties"里的cdkey字段
        // 生成授权文件
        // Date endDate = DateUtil.offsetMonth(new Date(), 12); // 有效期1年
        // String sellername = "";
        // String taxno = "";
        // String license = buildLicense(sellername, taxno, new Date(), endDate, "1");
        // String filePath = "/Users/gongquanlin/Documents/航天信息/ys/BZ" + taxno + ".Render";
        // FileUtil.writeBytes(license.getBytes("GBK"), filePath);
        //
        // Map<String, String> config = readFile(filePath, taxno);
        // System.out.println("config is " + JSON.toJSONStringWithDateFormat(config, "yyyy-MM-dd HH:mm:ss", new SerializerFeature[0]));
        // System.out.println("output is " + filePath);
    }

    public static void convertFileToGBK(String input, String output) {
        File file = new File(input);
        File realFile = new File(output);
        String codeSt = codeString(file);
        try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), codeSt)); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(realFile), "gbk"))) {
            String line = "";
            while ((line = in.readLine()) != null) {
                out.write(line + "\r\n");
            }
        } catch (IOException e) {
            System.out.println("Read or write Exceptioned");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String codeString(File file) {
        String code = null;
        try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file))) {
            int p = (bin.read() << 8) + bin.read();
            switch (p) {
                case 0xefbb:
                    code = "UTF-8";
                    break;
                case 0xfffe:
                    code = "Unicode";
                    break;
                case 0xfeff:
                    code = "UTF-16BE";
                    break;
                default:
                    code = "GBK";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return code;
    }


    /**
     * 生成ys授权license
     *
     * @param username  用户名
     * @param taxno     税号
     * @param startTime 开始时间
     * @param endTime   授权结束时间
     * @param machineNo 开票机号string，目前没看明白ys是怎么区分的，但是会index判断授权。在SetServiceImpl的44行，"if (rtn.get("result").toString().equals("1") && rtn.get("limit").toString().indexOf(changes) > -1) {"
     */
    public static String buildLicense(String username, String taxno, Date startTime, Date endTime, String machineNo) throws UnsupportedEncodingException {
        String startDate = DateUtil.format(startTime, "yyyyMMdd");
        String endDate = DateUtil.format(endTime, "yyyyMMdd");
        String xml = "<?xml version=\"1.0\" encoding=\"GB2312\" standalone=\"no\"?>" +
                "<RENDER-INFO>" +
                "<RENDER-TAXCODE>" + taxno + "</RENDER-TAXCODE>" +
                "<RENDER-NAME>" + username + "BZ</RENDER-NAME>" +
                "<RENDER-START>" + startDate + "</RENDER-START>" + // 开始时间
                "<RENDER-CEASE>" + endDate + "BZ</RENDER-CEASE>" + // 结束时间
                "<RENDER-LIMIT>" + machineNo + "</RENDER-LIMIT>" + // 开票机号
                "</RENDER-INFO>";
        String encodeResult = encode(xml);
        return encodeResult;
    }

    /**
     * ys授权文件
     *
     * @param filePath 授权证书路径
     * @param nsrsbh   纳税人识别号
     * @return
     */
    public static Map<String, String> readFile(String filePath, String nsrsbh) {
        Map map = new HashMap();
        StringBuilder sb = new StringBuilder();
        File file = new File(filePath);
        try {
            if (file.isFile() && file.exists()) {
                BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "GBK"));
                while (true) {
                    String sLine = br.readLine();
                    if (sLine == null) {
                        break;
                    }
                    sb.append(sLine);
                }
                br.close();
                String beResult = decode(sb.toString()); // 解密函数入口
                map = a(beResult, "BZ", nsrsbh); // 读取授权配置
            } else {
                map.put("result", "2");
                map.put("msg", "授权文件不存在！");
            }
        } catch (Exception e) {
        }
        return map;
    }


    /**
     * ys的配置项解密
     *
     * @param b
     * @return
     */
    private static byte[] decodeByte(byte[] b) {
        if (b.length % 2 != 0) {
            throw new IllegalArgumentException("长度不是偶数");
        }
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);
            byte temp = (byte) Integer.parseInt(item, 16);
            b2[n / 2] = temp;
        }
        return b2;
    }


    /**
     * ys的配置项解密
     *
     * @param src
     * @param key
     * @return
     * @throws Exception
     */
    private static byte[] desDecode(byte[] src, byte[] key) throws Exception {
        SecureRandom sr = new SecureRandom();
        DESKeySpec dks = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
        return cipher.doFinal(src);
    }

    /**
     * bytp转16进制字符串
     *
     * @param bytes
     * @return
     */
    public static String encodeByte(byte[] bytes) {
        StringBuilder buf = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) { // 使用String的format方法进行转换
            buf.append(String.format("%02x", new Integer(b & 0xff)));
        }

        return buf.toString();
    }

    /**
     * byte转16进制 string
     *
     * @param b2
     * @return
     */
    private static String byteToHexString(byte b2) {
        String hex = Integer.toHexString(b2 & 0xFF);
        if (hex.length() < 2) {
            hex = "0" + hex;
        }
        hex = hex.toUpperCase();
        return hex;
    }

    /**
     * ys的配置项des加密
     *
     * @param src
     * @param key
     * @return
     * @throws Exception
     */
    private static byte[] desEncode(byte[] src, byte[] key) throws Exception {
        SecureRandom sr = new SecureRandom();
        DESKeySpec dks = new DESKeySpec(key);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
        return cipher.doFinal(src);
    }


    private static Map<String, String> a(String protocolXML, String code, String nsrsbh) {
        Map rtn = new HashMap();
        try {
            Map map = c(protocolXML);
            if (map != null) {
                if (map.get("RENDER-NAME").toString().indexOf(code) > -1 && map.get("RENDER-CEASE").toString().indexOf(code) > -1 && map.get("RENDER-TAXCODE").toString().equals(nsrsbh)) {
                    String name = map.get("RENDER-NAME").toString().replaceAll(code, "");
                    String cease = map.get("RENDER-CEASE").toString().replaceAll(code, "");
                    if (d(getNowtime("yyyyMMdd"), map.get("RENDER-START").toString()) < 0 || d(getNowtime("yyyyMMdd"), cease) > 0) {
                        rtn.put("result", "2");
                        rtn.put("msg", "");
                    } else {
                        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
                        Date now = df.parse(getNowtime("yyyyMMdd"));
                        Date date = df.parse(cease);
                        rtn.put("clientname", name);
                        rtn.put("limit", map.get("RENDER-LIMIT").toString());
                        long day = (date.getTime() - now.getTime()) / 86400000;
                        if (day > 0) {
                            rtn.put("result", "1");
                            if (day < 60) {
                                rtn.put("msg", "");
                            } else {
                                rtn.put("msg", "");
                            }
                        } else if (day == 0) {
                            rtn.put("result", "1");
                            rtn.put("msg", "");
                        } else {
                            rtn.put("result", "2");
                            rtn.put("msg", "");
                        }
                    }
                    rtn.put("endday", cease);
                } else {
                    rtn.put("result", "0");
                    rtn.put("msg", "");
                }
            } else {
                rtn.put("result", "0");
                rtn.put("msg", "");
            }
        } catch (Exception e) {
        }
        return rtn;
    }


    private static Map<String, String> c(String protocolXML) {
        Map map = new HashMap();
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(protocolXML)));
            Element root = doc.getDocumentElement();
            NodeList nodes = root.getChildNodes();
            if (nodes != null) {
                for (int i = 0; i < nodes.getLength(); i++) {
                    Node node = nodes.item(i);
                    map.put(node.getNodeName(), node.getFirstChild().getNodeValue());
                }
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return map;
    }

    private static int d(String DATE1, String DATE2) {
        DateFormat df = new SimpleDateFormat("yyyyMMdd");
        try {
            Date dt1 = df.parse(DATE1);
            Date dt2 = df.parse(DATE2);
            if (dt1.getTime() > dt2.getTime()) {
                return 1;
            }
            if (dt1.getTime() < dt2.getTime()) {
                return -1;
            }
            return 0;
        } catch (Exception e) {
            return 0;
        }
    }

    private static String getNowtime(String tem) {
        return new SimpleDateFormat(tem).format(Calendar.getInstance().getTime());
    }


    /**
     * 解密函数入口
     *
     * @param data
     * @return
     */
    private static String decode(String data) {
        try {
            byte[] aResult = decodeByte(data.getBytes());
            byte[] decodeResult = desDecode(aResult, "SHAN@DONG#HANG$T%X?X".getBytes());
            return new String(decodeResult);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 加密函数入口
     *
     * @param data
     * @return
     */
    private static String encode(String data) {
        try {
            byte[] decodeResult = desEncode(data.getBytes("GBK"), "SHAN@DONG#HANG$T%X?X".getBytes());
            return encodeByte(decodeResult);
        } catch (Exception e) {
            return null;
        }
    }

}
