package com.keyware.shandan.bianmu.export;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.keyware.shandan.bianmu.entity.DirectoryResource;
import com.keyware.shandan.bianmu.entity.MetadataBasicVo;
import com.keyware.shandan.bianmu.service.MetadataService;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.common.util.StringUtils;
import com.keyware.shandan.frame.config.component.AppContext;
import com.keyware.shandan.system.entity.SysFile;
import com.keyware.shandan.system.service.SysFileService;
import com.keyware.shandan.system.utils.SysSettingUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class DirectoryExport extends ExportProgress {
    private final List<DirectoryResource> dataList;
    private final SysFileService fileService;
    private final MetadataService metadataService;
    private final String outputDir = "目录导出_" + DateUtil.format(new Date(), "yyyyMMddHHmmss");
    private final String outputPath = tempDir + File.separator + outputDir;
    private final String zipName = outputPath + ".zip";

    public DirectoryExport(String userId, List<DirectoryResource> dataList) {
        super(userId);
        this.dataList = dataList;
        this.fileService = AppContext.getContext().getBean(SysFileService.class);
        this.metadataService = AppContext.getContext().getBean(MetadataService.class);
    }

    @Override
    public void run() {
        try {
            this.setTitle("正在生成临时数据文件");
            dataList.forEach(resource -> {
                if ("file".equals(resource.getResourceType())) {
                    exportFile(resource);
                } else {
                    exportMetadata(resource);
                }
                this.autoAddStep();
            });
            this.setTitle("正在打包数据文件");
            ZipUtil.zip(outputPath, zipName, true);
            this.autoAddStep();
            this.setDone();
            this.setTitle("数据包准备完毕，开始下载");
        } catch (Exception e) {
            throw new RuntimeException("导出数据在打包时出错", e);
        } finally {
            delete(outputPath);
        }
    }

    /**
     * 导出文件
     *
     * @param resource 数据资源
     */
    private void exportFile(DirectoryResource resource) {
        File serverFile = new File(createResourceDir(resource.getDirectoryPath()));
        SysFile file = fileService.getById(resource.getId());
        String fileDownloadUrl = SysSettingUtil.getSysSetting("BIANMU").getSysInternalAddress() + file.getWebUri();

        try {
            HttpUtil.downloadFile(fileDownloadUrl, serverFile);
        } catch (Exception e) {
            List<String> lines = new ArrayList<>();
            String dirPath = serverFile.getParentFile().getPath();
            String name = "(文件读取错误)" + serverFile.getName();
            FileUtil.writeLines(lines, new File(dirPath, name), "utf-8");
        }
    }

    /**
     * 导出数据表
     *
     * @param resource 数据资源
     */
    private void exportMetadata(DirectoryResource resource) {
        File serverFile = new File(createResourceDir(resource.getDirectoryPath()));
        String path = serverFile.getPath() + ".xlsx";
        MetadataBasicVo metadata = null;
        JSONArray colsArray = null;
        List<Map<String, Object>> datas = null;

        try {
            metadata = metadataService.getById(resource.getId());
            colsArray = metadataService.getColumns(metadata.getId());
            datas = metadataService.getDynamicData(metadata);
        } catch (Exception e) {
            colsArray = new JSONArray();
            JSONObject defaultJson = new JSONObject();
            defaultJson.put("comment", "数据查询失败，无法连接到指定的数据库");
            colsArray.add(defaultJson);
            datas = new ArrayList<>();

            File temp = new File(path);
            String dirPath = temp.getParentFile().getPath();
            path = dirPath + File.separator + "(数据读取错误)" + temp.getName();
        }

        // 所有行的集合
        List<List<String>> rowList = new ArrayList<>();
        // 字段列名集合
        List<String> colNameList = new ArrayList<>();
        // 字段列名注释集合，当做表格的第一行
        List<String> columns = StreamUtil.as(colsArray).map(json -> {
            JSONObject col = (JSONObject) json;
            String comment = col.getString("comment");
            String colName = col.getString("columnName");
            colNameList.add(colName);
            return colName + (StringUtils.hasText(comment) ? "[" + comment + "]" : "");
        }).toList();

        // 添加第一行
        rowList.add(columns);

        // 遍历数据
        datas.forEach(data -> {
            // 遍历字段列名，获取对应数据并拼接成列的集合
            List<String> cells = colNameList.stream().map(col -> {
                Object value = data.get(col);
                if (value == null) {
                    return "";
                }
                return String.valueOf(value);
            }).collect(Collectors.toList());
            rowList.add(cells);
        });

        List<List<String>> rows = CollUtil.newArrayList(rowList);
        //通过工具类创建writer
        BigExcelWriter writer = ExcelUtil.getBigWriter(path);
        //一次性写出内容
        writer.write(rows);
        //关闭writer，释放内存
        writer.close();
    }

    /**
     * 根据资源路径创建目录
     *
     * @param resourcePath 资源路径
     * @return 资源的的绝对路径
     */
    private String createResourceDir(String resourcePath) {
        String path = outputPath + File.separator + resourcePath;
        File file = new File(path);
        File parent = file.getParentFile();
        if (!parent.exists()) {
            parent.mkdirs();
        }
        return path;
    }

    private void delete(String path) {
        File file = new File(path);
        if (file.exists()) {
            FileUtil.del(file);
        }
    }

    @Override
    public long getStepTotal() {
        return dataList.size() + 1;
    }

    @Override
    public String getDownloadPath() {
        return zipName;
    }
}
