package cn.stylefeng.workflow.instance.modular.export.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.stylefeng.roses.kernel.conversion.util.ExcelOperateUtil;
import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil;
import cn.stylefeng.roses.kernel.rule.util.ResponseRenderUtil;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.team.form.api.FormItemFormatApi;
import cn.stylefeng.team.form.api.FormModelServiceApi;
import cn.stylefeng.team.form.api.entity.FormItem;
import cn.stylefeng.team.form.api.pojo.FormDesignVo;
import cn.stylefeng.team.project.api.pojo.BusinessRenderDetailVo;
import cn.stylefeng.workflow.api.DefinitionApi;
import cn.stylefeng.workflow.api.entity.design.Definition;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.enums.TaskStatusEnum;
import cn.stylefeng.workflow.api.enums.TaskTypeEnum;
import cn.stylefeng.workflow.api.pojo.config.SixGraphDesign;
import cn.stylefeng.workflow.api.pojo.config.model.ModelForm;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.export.factory.ExportInstanceFactory;
import cn.stylefeng.workflow.instance.modular.export.service.InstanceExportService;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeInstanceService;
import cn.stylefeng.workflow.instance.modular.task.pojo.ApprovalLog;
import cn.stylefeng.workflow.instance.modular.task.pojo.request.TaskFormDataRequest;
import cn.stylefeng.workflow.instance.modular.task.service.ApprovalLogService;
import cn.stylefeng.workflow.instance.modular.task.service.RuTaskService;
import com.aspose.cells.*;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class InstanceExportServiceImpl implements InstanceExportService {

    @Resource
    private RuntimeInstanceService runtimeInstanceService;

    @Resource
    private DefinitionApi definitionApi;

    @Resource
    private FormModelServiceApi formModelServiceApi;

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private ApprovalLogService approvalLogService;

    @Resource
    private SysUserServiceApi sysUserServiceApi;

    @Resource
    private FormItemFormatApi formItemFormatApi;

    @Override
    public void exportInstance(Long instanceId) {

        RuntimeInstance runtimeInstance = runtimeInstanceService.getInstanceInfById(instanceId);

        Map<String, FormItem> formItemList = this.getFormItemList(runtimeInstance);

        TaskFormDataRequest taskFormDataRequest = new TaskFormDataRequest();
        taskFormDataRequest.setInstanceId(instanceId);
        BusinessRenderDetailVo taskFormData = ruTaskService.getTaskFormData(taskFormDataRequest);

        RuTaskRequest ruTaskRequest = new RuTaskRequest();
        ruTaskRequest.setInstanceId(instanceId);
        List<ApprovalLog> summaryApprovalLog = approvalLogService.getSummaryApprovalLog(ruTaskRequest);

        this.createExcel(runtimeInstance, formItemList, taskFormData, summaryApprovalLog);
    }

    private Map<String, FormItem> getFormItemList(RuntimeInstance runtimeInstance) {

        Long definitionId = runtimeInstance.getDefinitionId();
        Definition definitionInfo = definitionApi.getDefinitionInfo(definitionId);

        SixGraphDesign xSixGraphJson = definitionInfo.getXSixGraphJson();
        List<ModelForm> formDefinitionList = xSixGraphJson.getFlowGlobalConfig().getFormDefinitionList();

        List<Long> formDefinitionId = formDefinitionList.stream().map(ModelForm::getFormDefinitionId).collect(Collectors.toList());
        List<FormDesignVo> formDesignVoList = formModelServiceApi.getFromDesignList(formDefinitionId);

        return ExportInstanceFactory.getFormItemMap(formDesignVoList);
    }

    private void createExcel(RuntimeInstance runtimeInstance,
                             Map<String, FormItem> formItemList,
                             BusinessRenderDetailVo taskFormData,
                             List<ApprovalLog> summaryApprovalLog) {

        Workbook workbook = null;
        try {
            workbook = new Workbook(ResourceUtil.getStream("flow-export/empty-export.xlsx"));

            Worksheet sheet = workbook.getWorksheets().get(0);

            setInstanceTitle(runtimeInstance, formItemList, sheet);

            setFormItemColTitle(formItemList, sheet);

            setFormData(formItemList, taskFormData, sheet);

            setApprovalLogTitle(sheet);

            setApprovalLogList(summaryApprovalLog, sheet);

            sheet.autoFitColumns();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        HttpServletResponse response = HttpServletUtil.getResponse();
        ResponseRenderUtil.setRenderExcelHeader(response, "流程实例导出");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            workbook.save(outputStream, SaveFormat.XLSX);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IoUtil.close(outputStream);
        }
    }

    private static void setInstanceTitle(RuntimeInstance runtimeInstance, Map<String, FormItem> formItemList, Worksheet sheet) {

        int size = formItemList.size();
        String colIndex = ExcelUtil.indexToColName(size - 1);

        Cells cells = sheet.getCells();
        cells.merge(0, 0, 1, size);

        cells.setRowHeight(1, 30);

        ExcelOperateUtil.setCellTextCenter(cells, "A1");

        cells.get("A1").setValue(runtimeInstance.getInstanceTitle());

        ExcelOperateUtil.setRangeBorder(sheet, "A1:" + colIndex + "3");
    }

    private static void setApprovalLogTitle(Worksheet sheet) {

        Range range = sheet.getCells().createRange("A6:I6");
        range.merge();

        sheet.getCells().get("A6").setValue("审批日志");

        ExcelOperateUtil.setCellTextCenter(sheet.getCells(), "A6");

        sheet.getCells().setRowHeight(5, 25);

        String[] approvalLogTitle = new String[]{"序号", "节点名称", "处理人", "节点类型", "任务状态", "开始时间", "结束时间", "时长", "审批意见"};
        ExcelOperateUtil.setSheetData(sheet, 7, 1, approvalLogTitle);
    }

    private static void setFormItemColTitle(Map<String, FormItem> formItemList, Worksheet sheet) {
        LinkedList<String> columnTitleList = new LinkedList<>();
        for (Map.Entry<String, FormItem> entry : formItemList.entrySet()) {
            String label = entry.getValue().getItemLabelName();
            columnTitleList.add(label);
        }
        String[] columnLabelList = ArrayUtil.toArray(columnTitleList, String.class);
        ExcelOperateUtil.setSheetData(sheet, 2, 1, columnLabelList);
    }

    private void setFormData(Map<String, FormItem> formItemList, BusinessRenderDetailVo taskFormData, Worksheet sheet) {
        LinkedList<String> formDataList = new LinkedList<>();

        for (Map.Entry<String, FormItem> fieldInfo : formItemList.entrySet()) {
            String columnFieldCode = fieldInfo.getKey();
            FormItem formItem = fieldInfo.getValue();
            for (Map.Entry<String, Map<String, Object>> manyFormEntrySet : taskFormData.getFormDetail().entrySet()) {
                Map<String, Object> thisFormData = manyFormEntrySet.getValue();
                for (Map.Entry<String, Object> formItemCodeValue : thisFormData.entrySet()) {
                    String fieldCode = formItemCodeValue.getKey();
                    if (columnFieldCode.equals(fieldCode)) {

                        Object value = formItemCodeValue.getValue();
                        if (ObjectUtil.isEmpty(value)) {
                            formDataList.add("");
                        } else {
                            Object tempValue = formItemCodeValue.getValue();
                            Object format = formItemFormatApi.format(formItem.getComponentCode(), formItem, tempValue);
                            formDataList.add(String.valueOf(format));
                        }
                    }
                }
            }
        }

        String[] formDataArray = ArrayUtil.toArray(formDataList, String.class);
        ExcelOperateUtil.setSheetData(sheet, 3, 1, formDataArray);
    }

    private void setApprovalLogList(List<ApprovalLog> summaryApprovalLog, Worksheet sheet) {

        for (int i = 0; i < summaryApprovalLog.size(); i++) {
            List<String> logLineValue = new ArrayList<>();

            logLineValue.add(String.valueOf(i + 1));

            String nodeName = summaryApprovalLog.get(i).getNodeName();
            if (ObjectUtil.isNotEmpty(nodeName)) {
                logLineValue.add(nodeName);
            } else {
                logLineValue.add("");
            }

            String userRealName = sysUserServiceApi.getUserRealName(summaryApprovalLog.get(i).getProcessUserId());
            logLineValue.add(userRealName);

            Integer taskType = summaryApprovalLog.get(i).getTaskType();
            if (ObjectUtil.isNotEmpty(taskType)) {
                TaskTypeEnum taskTypeEnum = TaskTypeEnum.START.parseToEnum(String.valueOf(taskType));
                if (taskTypeEnum != null) {
                    logLineValue.add(taskTypeEnum.getName().toString());
                } else {
                    logLineValue.add("");
                }
            } else {
                logLineValue.add("");
            }

            Integer taskStatus = summaryApprovalLog.get(i).getTaskStatus();
            if (ObjectUtil.isNotEmpty(taskStatus)) {
                TaskStatusEnum taskStatusEnum = TaskStatusEnum.START.parseToEnum(String.valueOf(taskStatus));
                if (taskStatusEnum != null) {
                    logLineValue.add(taskStatusEnum.getName().toString());
                } else {
                    logLineValue.add("");
                }
            } else {
                logLineValue.add("");
            }

            Date beginTime = summaryApprovalLog.get(i).getBeginTime();
            if (ObjectUtil.isNotEmpty(beginTime)) {
                logLineValue.add(DateUtil.formatDateTime(beginTime));
            } else {
                logLineValue.add("");
            }

            Date approveTime = summaryApprovalLog.get(i).getApproveTime();
            if (ObjectUtil.isNotEmpty(approveTime)) {
                logLineValue.add(DateUtil.formatDateTime(approveTime));
            } else {
                logLineValue.add("");
            }

            String durationFormat = summaryApprovalLog.get(i).getDurationFormat();
            if (ObjectUtil.isNotEmpty(durationFormat)) {
                logLineValue.add(durationFormat);
            } else {
                logLineValue.add("");
            }

            String suggestContent = summaryApprovalLog.get(i).getSuggestContent();
            if (ObjectUtil.isNotEmpty(suggestContent)) {
                logLineValue.add(suggestContent);
            } else {
                logLineValue.add("");
            }

            String[] logLineArray = ArrayUtil.toArray(logLineValue, String.class);
            ExcelOperateUtil.setSheetData(sheet, 7 + (i + 1), 1, logLineArray);
        }

        ExcelOperateUtil.setRangeBorder(sheet, "A6:I" + (summaryApprovalLog.size() + 7));
    }

}
