package com.keyware.shandan.dynacmicform.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.keyware.shandan.common.entity.Result;
import com.keyware.shandan.common.util.FileDownload;
import com.keyware.shandan.common.util.StreamUtil;
import com.keyware.shandan.dynacmicform.core.basic.entity.DynamicData;
import com.keyware.shandan.dynacmicform.core.basic.entity.FormInfoPo;
import com.keyware.shandan.dynacmicform.core.basic.entity.QueryParam;
import com.keyware.shandan.dynacmicform.core.basic.services.DynamicDataService;
import com.keyware.shandan.dynacmicform.core.basic.services.FormInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 动态数据前端视图、接口控制器
 */
@Slf4j
@RestController
@RequestMapping("dynamic-data")
public class DynamicDataController {

    @Autowired
    private FormInfoService formInfoService;

    @Autowired
    private DynamicDataService dynamicDataService;


    /**
     * 动态数据视图 - 指定表单编辑视图
     *
     * @param mav    视图
     * @param formId 表单ID
     * @param id     数据ID
     * @return 视图
     * @throws Exception e
     */
    @GetMapping("/view/data/edit/{formId}")
    public ModelAndView dataListDefaultView(ModelAndView mav, @PathVariable String formId, Integer id) {
        mav.setViewName("dynamicData/dataEdit");
        try {
            FormInfoPo formInfo = formInfoService.getById(formId);
            Assert.notNull(formInfo, "没有查询到表单数据");
            mav.addObject("formInfo", formInfo);

            if (id != null) {
                mav.addObject("dataInfo", dynamicDataService.getById(formInfo, id));
            }
        } catch (Exception e) {
            mav.addObject("error", true);
            mav.addObject("msg", e.getMessage());
        }
        return mav;
    }

    /**
     * 视图 - 表单预览视图
     *
     * @return 视图
     */
    @GetMapping("/view/data/preview/{formId}")
    public ModelAndView previewView(ModelAndView mav, @PathVariable String formId, Integer id) {
        mav.setViewName("dynamicData/dataPreview");
        try {
            FormInfoPo formInfo = formInfoService.getById(formId);
            Assert.notNull(formInfo, "没有查询到表单数据");
            mav.addObject("formInfo", formInfo);

            Assert.notNull(id, "数据主键id为空，请检查数据");
            mav.addObject("dataInfo", dynamicDataService.getById(formInfo, id));
        } catch (Exception e) {
            mav.addObject("error", true);
            mav.addObject("msg", e.getMessage());
        }
        return mav;
    }

    /**
     * 动态数据视图 - 指定表单的数据列表视图
     *
     * @param mav 视图
     * @return 视图
     */
    @GetMapping("/view/data/list/")
    public ModelAndView dataListView(ModelAndView mav) {
        mav.addObject("formInfo", new FormInfoPo());
        mav.addObject("preview", false);
        mav.setViewName("dynamicData/dataList");
        return mav;
    }

    @GetMapping("/view/data/list/{formId}")
    public ModelAndView formDataListView(ModelAndView mav, @PathVariable Integer formId, @RequestParam(required = false) boolean preview) {
        mav.setViewName("dynamicData/dataList");
        mav.addObject("preview", preview);
        mav.addObject("formInfo", formInfoService.getById(formId));
        return mav;
    }

    /**
     * 动态数据接口 - 分页查询指定表单的数据列表
     *
     * @param request 请求
     * @param formId  表单ID
     * @param current 当前页
     * @param size    页大小
     * @return 数据列表
     */
    @PostMapping("/api/data/list/{formId}")
    public Result<Page<Map<String, Object>>> dataList(HttpServletRequest request, @PathVariable String formId, int current, int size) throws Exception {
        String param = request.getParameter("param");
        String order = request.getParameter("order");
        QueryParam queryParam = QueryParam.parse(param);
        queryParam.setPage(current);
        queryParam.setPageSize(size);
        queryParam.parseOrder(order);
        FormInfoPo formInfo = formInfoService.getById(formId);
        return Result.of(dynamicDataService.pageList(formInfo, queryParam));
    }

    /**
     * 动态数据接口 - 查询指定表单的单挑数据详细信息
     *
     * @param formId 表单ID
     * @param id     数据ID
     * @return 数据信息
     * @throws Exception -
     */
    @GetMapping("/api/data/get/{formId}")
    public Result<Object> dataGet(@PathVariable String formId, Integer id) throws Exception {
        FormInfoPo formInfoPo = formInfoService.getById(formId);
        Assert.notNull(formInfoPo, "查询数据详细失败，数据库中没有找到相关表单信息");
        return Result.of(dynamicDataService.getById(formInfoPo, id));
    }

    @PostMapping("/api/data/save/{formId}")
    public Result<Object> dataSave(@PathVariable String formId, DynamicData data) throws Exception {
        FormInfoPo form = formInfoService.getById(formId);
        Assert.notNull(form, "没有找到相关表单信息");
        return Result.of(dynamicDataService.saveOrUpdate(form, data.getData()));
    }

    /**
     * 动态数据接口 - 删除指定表单的单条数据
     *
     * @param formId 表单ID
     * @param id     数据ID
     * @return 结果集
     * @throws Exception -
     */
    @PostMapping("/api/data/delete/{formId}")
    public Result<Object> dataDelete(@PathVariable String formId, String id) throws Exception {
        FormInfoPo formInfoPo = formInfoService.getById(formId);
        Assert.notNull(formInfoPo, "查询数据详细失败，数据库中没有找到相关表单信息");
        if (StringUtils.hasText(id)) {
            for (String _id : id.split(",")) {
                dynamicDataService.deleteById(formInfoPo, Integer.valueOf(_id));
            }
            return Result.of(null);
        } else {
            return Result.of(null, false, "参数错误");
        }
    }

    /**
     * 下载表单的Excel导入模板
     *
     * @param response
     * @param formId
     */
    @GetMapping("/api/data/download/template/{formId}")
    public String downloadTemplate(HttpServletResponse response, @PathVariable String formId) {
        FormInfoPo formInfo = formInfoService.getById(formId);
        Assert.notNull(formInfo, "表单信息不存在");
        File file = generateExcelTemplate(formInfo);
        return FileDownload.download(response, file, formInfo.getFormName() + ".xlsx", true);
    }

    /**
     * 下载Excel数据表格
     *
     * @param response
     * @param formId
     * @throws Exception
     */
    @GetMapping("/api/data/download/excel/{formId}")
    public String exportExcel(HttpServletResponse response, @PathVariable String formId) throws Exception {
        FormInfoPo formInfo = formInfoService.getById(formId);
        Assert.notNull(formInfo, "表单信息不存在");
        File file = generateDatExcel(formInfo);
        return FileDownload.download(response, file, formInfo.getFormName() + ".xlsx", true);
    }

    /**
     * 导入Excel数据表格
     *
     * @param formId
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/api/data/import/excel/{formId}")
    public Result<Object> importExcel(@PathVariable String formId, MultipartFile file) throws IOException {
        FormInfoPo formInfo = formInfoService.getById(formId);
        Assert.notNull(formInfo, "表单信息不存在");
        ExcelUtil.readBySax(file.getInputStream(), -1, excelRowDataHandler(formInfo));
        return Result.of(null);
    }

    /**
     * Excel数据行解析函数
     *
     * @param formInfo
     * @return
     */
    private RowHandler excelRowDataHandler(FormInfoPo formInfo) {
        List<String> tableCols = StreamUtil.as(getFormColumns(formInfo)).map(item -> item.get("column_id")).collect(Collectors.toList());
        List<String> rowHeader = new ArrayList<>();
        return (sheetIndex, rowIndex, rowList) -> {
            List<String> strRows = null;
            if (rowIndex == 1) {
                rowHeader.clear();
                rowHeader.addAll(parseExcelRowData(rowList));
            } else if (rowIndex >= 3) {
                strRows = parseExcelRowData(rowList);
                Map<String, Object> map = new HashMap<>();
                for (int i = 0; i < rowHeader.size(); i++) {
                    String head = rowHeader.get(i);
                    if (tableCols.contains(head)) {
                        map.put(head, strRows.get(i));
                    } else {
                        log.warn("基础数据导入时，数据行解析异常，数据库中没有该字段：{}，已丢弃；表单：{}({}),数据：{}", head, formInfo.getFormId(), formInfo.getFormName(), String.join(",", strRows));
                    }
                }
                try {
                    dynamicDataService.saveOrUpdate(formInfo, map);
                } catch (Exception e) {
                    log.error("基础数据导入时，数据插入异常，表单：{}({}),数据：{}", formInfo.getFormId(), formInfo.getFormName(), String.join(",", strRows));
                }
                System.out.println("formInfo = " + formInfo);
            }
        };
    }

    /**
     * 生成包含数据的Excel
     *
     * @param formInfo
     * @return
     * @throws Exception
     */
    private File generateDatExcel(FormInfoPo formInfo) throws Exception {
        List<Object> colIds = new ArrayList<>();
        List<Object> colNames = new ArrayList<>();
        getFormColumns(formInfo).forEach(item -> {
            colIds.add(item.get("column_id"));
            colNames.add(item.get("column_name"));
        });
        List<List<Object>> rows = CollUtil.newArrayList(colIds, colNames);

        // 加入数据
        List<Map<String, Object>> dataList = dynamicDataService.list(formInfo);
        Assert.isTrue(dataList.size() > 0, "没有可以导出的数据");
        dataList.forEach(item -> {
            List<Object> tempList = new ArrayList<>();
            colIds.forEach(id -> {
                tempList.add(item.get(id));
            });
            rows.add(tempList);
        });

        String filePath = System.getProperty("user.home") + File.separator + formInfo.getFormId() + ".xlsx";
        File file = new File(filePath);
        if(file.exists()){
            file.delete();
        }
        ExcelWriter excelWriter = ExcelUtil.getWriter(file);
        excelWriter.merge(colIds.size() - 1, formInfo.getFormName());
        excelWriter.write(rows, true);
        excelWriter.close();
        return file;
    }

    /**
     * 生成Excel模板
     *
     * @param formInfo
     * @return
     */
    private File generateExcelTemplate(FormInfoPo formInfo) {
        List<String> colIds = new ArrayList<>();
        List<String> colNames = new ArrayList<>();
        getFormColumns(formInfo).forEach(item -> {
            colIds.add(item.get("column_id"));
            colNames.add(item.get("column_name"));
        });
        List<List<String>> rows = CollUtil.newArrayList(colIds, colNames);

        String filePath = System.getProperty("user.home") + File.separator + formInfo.getFormId() + "_template.xlsx";
        File file = new File(filePath);
        if(file.exists()){
            file.delete();
        }
        ExcelWriter excelWriter = ExcelUtil.getWriter(file);
        excelWriter.merge(colIds.size() - 1, formInfo.getFormName());
        excelWriter.write(rows, true);
        excelWriter.close();
        return file;
    }

    /**
     * 获取表单信息的字段集合
     *
     * @param formInfo
     * @return
     */
    private List<Map<String, String>> getFormColumns(FormInfoPo formInfo) {
        Assert.hasText(formInfo.getFieldConfig(), "表单配置验证失败，缺少字段配置");
        JSONArray jsonArray = JSONArray.parseArray(formInfo.getFieldConfig());
        return StreamUtil.as(jsonArray).map(item -> {
            JSONObject json = (JSONObject) item;
            Map<String, String> map = new HashMap<>();
            map.put("column_id", json.getString("fieldId"));
            map.put("column_name", json.getString("label"));
            return map;
        }).toList();
    }

    /**
     * 解析Excel数据类型
     *
     * @param rowList
     * @return
     */
    private List<String> parseExcelRowData(List<Object> rowList) {
        return StreamUtil.as(rowList).map(row -> {
            if (row instanceof String) {
                return (String) row;
            } else if (row instanceof DateTime) {
                DateTime dateTime = (DateTime) row;
                return DateUtil.format(new Date(dateTime.getTime()), "yyyy-MM-dd HH:mm:ss");
            } else if (row instanceof Date) {
                return DateUtil.format((Date) row, "yyyy-MM-dd HH:mm:ss");
            } else if (row instanceof Long) {
                return ((Long) row).toString();
            } else if (row instanceof Double) {
                return ((Double) row).toString();
            } else {
                return row.toString();
            }
        }).collect(Collectors.toList());
    }
}
