package com.engine.htsc.payment.util;


import cn.hutool.extra.pinyin.PinyinUtil;
import com.alibaba.fastjson.JSONObject;
import com.engine.htsc.payment.pojo.ZipModel;
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;
import weaver.general.Util;
import weaver.interfaces.htsc.download.FileStream;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 附件下载
 */
public class ZipUtils extends BaseBean {

  /**
   * 根据分类目录获取文件集合
   *
   * @param categoryName 分类目录名称
   * @param parentPath   父路径
   * @return file list
   */
  public List<File> getFilesByCategory(String categoryName, String parentPath) throws Exception {
    RecordSet rs = new RecordSet();
    //按照目录路径配置
    String pinyinCategoryName = PinyinUtil.getFirstLetter(categoryName, "");
//    String sql = "SELECT t4.CATEGORYNAME,t3.IMAGEFILENAME,t3.FILEREALPATH,t3.IMAGEFILEID,t3.ISZIP,t2.DOCID,t3.IMAGEFILEID FROM DOCDETAIL t1\n" +
//            "LEFT JOIN DOCIMAGEFILE t2\n" +
//            "ON t1.ID = t2.DOCID \n" +
//            "LEFT JOIN IMAGEFILE t3\n" +
//            "ON t2.IMAGEFILEID = t3.IMAGEFILEID \n" +
//            "LEFT JOIN DOCSECCATEGORY t4\n" +
//            "ON t1.SECCATEGORY = t4.ID \n" +
//            "WHERE t4.CATEGORYNAME = ?";
//    rs.executeQuery(sql, categoryName);
//    List<ZipModel> zips = new ArrayList<>();
//    while (rs.next()) {
//      ZipModel zip = new ZipModel(parentPath + File.separator + pinyinCategoryName);
//      zip.setDocId(Util.null2String(rs.getString("DOCID")));
//      zip.setImageFileId(Util.null2String(rs.getString("IMAGEFILEID")));
//      zip.setRealName(Util.null2String(rs.getString("IMAGEFILENAME")));
//      zip.setRealPath(Util.null2String(rs.getString("FILEREALPATH")));
//      zip.setIsZip(Util.null2String(rs.getString("ISZIP")));
//      zips.add(zip);
//    }

    //从报销流程中获取数据 zbtybx,zbclbx,fzjgtybx,fzjgclbx
    List<String> tableUnNumber = Arrays.asList("zbtybx", "zbclbx", "fzjgtybx", "fzjgclbx");
    List<String> categoryFromWfTable = getCategoryFromWfTable(tableUnNumber, pinyinCategoryName);
    rs.writeLog("从报销流程中获取数据docid: zbtybx,zbclbx,fzjgtybx,fzjgclbx :" + JSONObject.toJSONString(categoryFromWfTable));
    List<ZipModel> zips = new ArrayList<>();
    for (int i = 0; i < categoryFromWfTable.size(); i += 998) {
      List<String> con = categoryFromWfTable.stream()
              .skip(i)
              .limit(998)
              .filter(StringUtils::isNotBlank)
              .collect(Collectors.toList());
      String sql = "SELECT t3.IMAGEFILENAME,t3.FILEREALPATH,t3.IMAGEFILEID,t3.ISZIP,t2.DOCID,t3.IMAGEFILEID FROM DOCDETAIL t1\n" +
              "LEFT JOIN DOCIMAGEFILE t2\n" +
              "ON t1.ID = t2.DOCID \n" +
              "LEFT JOIN IMAGEFILE t3\n" +
              "ON t2.IMAGEFILEID = t3.IMAGEFILEID \n" +
              "WHERE t2.DOCID in ('" + String.join(",", con).replace(",", "','") + "')";
      rs.executeQuery(sql);
      while (rs.next()) {
        ZipModel zip = new ZipModel(parentPath + File.separator + pinyinCategoryName);
        zip.setDocId(Util.null2String(rs.getString("DOCID")));
        zip.setImageFileId(Util.null2String(rs.getString("IMAGEFILEID")));
        zip.setRealName(Util.null2String(rs.getString("IMAGEFILENAME")));
        zip.setRealPath(Util.null2String(rs.getString("FILEREALPATH")));
        zip.setIsZip(Util.null2String(rs.getString("ISZIP")));
        zips.add(zip);
      }
    }

    //判断CATEGORYNAME下是否有附件
    if (CollectionUtils.isEmpty(zips)) {
      rs.writeLog(String.format("附件路径 %s 下没有附件,请检查目录是否维护", categoryName));
    }
    //相同附件取最新版本
    zips = zips.stream().sorted(Comparator.comparingInt(z -> Integer.parseInt(z.getImageFileId())))
            .filter(distinctByName(ZipModel::getDocId))
            .collect(Collectors.toList());
    //生成写入的路径
    String path = parentPath + File.separator + pinyinCategoryName; // /app/ecology/filesystem/temp/zip/分类拼音简称
    File dir = new File(path);
    if (dir.exists()) {
      dir.delete();
    }
    dir.setWritable(true, false);
    dir.mkdirs();
    Runtime.getRuntime().exec("chmod -R 777" + path);
    //生成目标文件集合
    List<File> sourceFile = new ArrayList<>();
    for (ZipModel zip : zips) {
      FileStream fileStream = new FileStream(zip.getRealPath(), "1");
      File file = new File(zip.getNewRealPathName());
      try (OutputStream os = new FileOutputStream(file)) {
        fileStream.write(os);
      }
      sourceFile.add(file);
    }
    return sourceFile;
  }

  /**
   * 获取打包的文件夹
   *
   * @param outputZipFile 输出zip文件
   * @param filesToAdd    需要打包的文件
   * @return zip file
   */
  public File zipFiles(File outputZipFile, List<File> filesToAdd) throws IOException {
    ZipParameters zipParameters = buildZipParameters(CompressionMethod.DEFLATE, CompressionLevel.NORMAL,
            false,
            EncryptionMethod.ZIP_STANDARD,
            null);
    byte[] buff = new byte[4096];
    int readLen;

    try (ZipOutputStream zos = initializeZipOutputStream(outputZipFile, false, null)) {
      for (File fileToAdd : filesToAdd) {
        if (zipParameters.getCompressionMethod() == CompressionMethod.STORE) {
          zipParameters.setEntrySize(fileToAdd.length());
        }

        zipParameters.setFileNameInZip(fileToAdd.getName());
        zos.putNextEntry(zipParameters);

        try (InputStream inputStream = new FileInputStream(fileToAdd)) {
          while ((readLen = inputStream.read(buff)) != -1) {
            zos.write(buff, 0, readLen);
          }
        }
        zos.closeEntry();
      }
      return outputZipFile;
    }
  }

  private ZipOutputStream initializeZipOutputStream(File outputZipFile, boolean encrypt, char[] password)
          throws IOException {
    FileOutputStream fos = new FileOutputStream(outputZipFile);
    if (encrypt) {
      return new ZipOutputStream(fos, password);
    }
    return new ZipOutputStream(fos);
  }

  private ZipParameters buildZipParameters(CompressionMethod compressionMethod, CompressionLevel compressionLevel,
                                           boolean encrypt,
                                           EncryptionMethod encryptionMethod, AesKeyStrength aesKeyStrength) {
    ZipParameters zipParameters = new ZipParameters();
    zipParameters.setCompressionMethod(compressionMethod);
    zipParameters.setCompressionLevel(compressionLevel);
    zipParameters.setEncryptionMethod(encryptionMethod);
    zipParameters.setAesKeyStrength(aesKeyStrength);
    zipParameters.setEncryptFiles(encrypt);
    return zipParameters;
  }

  private <T> Predicate<T> distinctByName(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
  }

  private List<String> getCategoryFromWfTable(List<String> tablesUnNumber, String categoryNamePinYin) {
    RecordSet rs = new RecordSet();
    rs.writeLog("table unnumber :" + tablesUnNumber);
    List<String> con = new ArrayList<>();
    String sql = "SELECT wm_concat_old(%s) AS docid FROM %s";
    for (String tableUnNumber : tablesUnNumber) {
      String table = PayUtil.getTableNameByUnNumber(tableUnNumber);
      rs.executeQuery(String.format(sql, categoryNamePinYin, table));
      if (rs.next()) {
        con.add(Util.null2String(rs.getString("docid")));
      }
    }
    return con;
  }
}
