package com.ruoyi.system.service.form;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.eazytec.form.tool.EformTool;
import com.eazytec.form.tool.parse.ParseField;
import com.eazytec.form.tool.parse.ParseForm;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.zqt.ZQTPageUtil;
import com.ruoyi.system.domain.form.MdForm;
import com.ruoyi.system.domain.form.MdFormRecord;
import com.ruoyi.system.domain.form.MdFormRecordExample;
import com.ruoyi.system.domain.form.enums.EnumFormMode;
import com.ruoyi.system.domain.form.enums.EnumFormPublishStatus;
import com.ruoyi.system.domain.form.enums.EnumFormType;
import com.ruoyi.system.domain.form.enums.EnumStartDateByQua;
import com.ruoyi.system.domain.form.params.FormRecordQuery;
import com.ruoyi.system.domain.form.params.FormUser;
import com.ruoyi.system.domain.form.vo.FormRecordListVo;
import com.ruoyi.system.domain.form.vo.FormRecordVo;
import com.ruoyi.system.mapper.form.MdFormRecordExtMapper;
import com.ruoyi.system.mapper.system.SysUserMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class FormRecordManager {

    @Autowired
    private MdFormRecordService recordService;

    @Autowired
    private MdFormRecordExtMapper extMdFormRecordMapper;

    @Autowired
    private FormManager formManager;

    @Autowired
    private MdFormService formService;

    @Autowired
    private FormMsgManager msgManager;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static ExecutorService pool = Executors.newFixedThreadPool(10);


    private static EformTool.Config toolConfig = new EformTool.Config();

    //表单自定义解析
    static {
        //表单组件解析
        toolConfig.setPrefix("M_");
        toolConfig.setId_field("id");
        toolConfig.setRecord_field("records");
        toolConfig.addFieldKeyHandler("M_commitCycle", s -> {
            return convertCycle(ObjectUtil.defaultIfNull(s, "").toString());
        });
        toolConfig.addFieldKeyHandler("M_source", s -> {
            return ObjectUtil.defaultIfNull(s, "线上填写").toString();
        });

    }

    /**
     * 批量插入
     */
    public int insertBatch(List<MdFormRecord> list) {
        if (CollectionUtils.isNotEmpty(list)) {
            return extMdFormRecordMapper.insertBatch(list);
        }
        return 0;
    }

    public void mind(String form_id) {
        msgManager.send4Mind(form_id, null, false);
    }


    public void mind(String form_id, Integer noRecord, Boolean isSend) {
        msgManager.send4Mind(form_id, noRecord, isSend);
    }

    /**
     * 获取表单设计解析器
     */
    public ParseForm getParseForm(String formId) {
        return getEformTool(formId).getParseForm();
    }

    public EformTool getEformTool(String formId) {
        MdForm form = formManager.detail(formId);
        if (form == null) {
            throw new RuntimeException("表单不存在");
        }
        if (StringUtils.isBlank(form.getFormConfig())) {
            throw new RuntimeException("表单解析失败");
        }

        EformTool tool = EformTool.build(form.getId(), form.getName(), form.getFormConfig());
        tool.config(toolConfig);
        tool.getParseForm().addExt("collectMode", form.getCollectMode());
        return tool;
    }

    /**
     * 获取表单标签
     */
    public Map<String, ParseField> formLabels(String formId) {
        return getEformTool(formId).formLabels();
    }

    /**
     * 查询未填写数据的用户
     */
    public PageInfo<FormUser> queryNoCommit(FormRecordQuery query) {
        query.setCommit_status(0);
        PageInfo<MdFormRecord> page = queryByPage(query);

        if (CollectionUtils.isEmpty(page.getList())) {
            return ZQTPageUtil.bulid(page.getList(), new ArrayList<>());
        }

        List<String> userIds = page.getList().stream().map(MdFormRecord::getOwnUser).collect(Collectors.toList());

        List<SysUser> sysUserList = sysUserMapper.listByIds(userIds);
        List<FormUser> formUsers = new ArrayList<>();
        for (SysUser sysUser : sysUserList) {
            FormUser formUser = new FormUser();
            formUser.setId(sysUser.getUserId());
            formUser.setName(sysUser.getNickName());
            formUsers.add(formUser);
        }
        return ZQTPageUtil.bulid(page.getList(), formUsers);
    }

    /**
     * 统计查询
     */
    public PageInfo<Map<String, Object>> queryCommit(FormRecordQuery query) {
        query.setCommit_status(1);
        PageInfo<FormRecordListVo> page = queryWithBodyByPage(query);
        return ZQTPageUtil.bulid(page.getList(), convert(page.getList()));
    }

    /**
     * 一般的基于userId来查询 待办已办
     *
     * @param query
     * @return
     */
    public PageInfo<FormRecordListVo> list(FormRecordQuery query) {
        PageInfo<FormRecordListVo> page = queryWithBodyByPage(query);
        return page;
    }


    /**
     * 所属周期
     */
    private static String convertCycle(String c) {
        if (StringUtils.isBlank(c)) {
            return "";
        }

        if (c.contains(EnumFormMode.ww.code)) {
            return c.replace(EnumFormMode.ww.code, "年第") + "周";
        } else if (c.contains(EnumFormMode.m.code)) {
            return c.replace(EnumFormMode.m.code, "年第") + "月";
        } else if (c.contains(EnumFormMode.q.code)) {
            return c.replace(EnumFormMode.q.code, "年第") + "季度";
        }
        return "";
    }

    /**
     * 统计查询 数据转换
     */
    private List<Map<String, Object>> convert(List<FormRecordListVo> list) {
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        String formId = list.get(0).getFormId();
        EformTool tool = getEformTool(formId);

        List<Map<String, Object>> res = new ArrayList<>();

        list.forEach(item -> res.add(tool.convert(item)));
        return res;
    }

    /**
     * 导出未填报用户
     */
    public void no_export(HttpServletResponse response, FormRecordQuery query) throws IOException {
//		普通表单：表单名称、未填写企业名称
//		周期表单：表单名称、周期维度、当前所处周期、未填写企业名称
        MdForm form = formManager.detail(query.getForm_id());

        if (form == null) {
            throw new RuntimeException("表单不存在");
        }

        //用户名称
        List<String> names = new ArrayList<>();
        int pageNum = 1;

        while (true) {

            FormRecordQuery recordQuery = new FormRecordQuery();
            recordQuery.setForm_id(query.getForm_id());
            recordQuery.setCommit_status(query.getCommit_status());
            recordQuery.setPageNum(pageNum);
            recordQuery.setPageSize(500);
            if (StringUtils.isNotBlank(query.getCommit_cycle())) {
                recordQuery.setCommit_cycle(query.getCommit_cycle());
            }
            recordQuery.setNoRecord(query.getNoRecord());
            PageInfo<FormUser> page = queryNoCommit(recordQuery);
            if (CollectionUtils.isEmpty(page.getList())) {
                break;
            }

            names.addAll(page.getList().stream().map(FormUser::getName).collect(Collectors.toList()));
            if (page.getSize() < 500) {
                break;
            }
            pageNum++;
        }

        List<List<String>> data = new ArrayList<>();

        String now_cycle = StringUtils.isBlank(query.getCommit_cycle()) ? form.getCycleNow() : query.getCommit_cycle();

        AtomicInteger order = new AtomicInteger(0);
        if (EnumFormType.cycle.text.equals(form.getCollectMode())) {
            data.add(Lists.newArrayList("序号", "表单名称", "周期维度", "当前所处周期", "未填写企业名称"));
            data.addAll(names.stream().map(item -> Lists.newArrayList(order.incrementAndGet() + "", form.getName(),
                    EnumFormMode.getByCode(form.getCycleType()).text,
                    convertCycle(now_cycle),
                    item)).collect(Collectors.toList()));
        } else {
            data.add(Lists.newArrayList("序号", "表单名称", "未填写企业名称"));
            data.addAll(names.stream().map(item -> Lists.newArrayList(order.incrementAndGet() + "", form.getName(),
                    item)).collect(Collectors.toList()));
        }

        ExcelWriter writer = ExcelUtil.getWriterWithSheet(form.getName());

        StyleSet style = writer.getStyleSet();
        style.setWrapText();

        IntStream.range(1, data.get(0).size()).boxed().forEach(i -> writer.setColumnWidth(i, 30));

        writer.write(data, true);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode(form.getName(), "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        ServletOutputStream out = response.getOutputStream();
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }


    /**
     * 导出已填报数据
     */
    public void export(HttpServletResponse response, FormRecordQuery query) throws IOException {
        EformTool tool = getEformTool(query.getForm_id());
        tool.addFieldKeyHandler("M_createTime", s -> {
            if (s != null) {
                return DatePattern.NORM_DATETIME_FORMAT.format(NumberUtils.toLong(s.toString()));
            }
            return null;
        });

        LinkedHashMap<String, String> headers = new LinkedHashMap<>();
        headers.put("M_userName", "提交人");
        headers.put("M_createTime", "填写时间");

        //子表格表头
        LinkedHashMap<String, String> sub_headers = new LinkedHashMap<>();
        sub_headers.put("M_userName", "提交人");

        if (EnumFormType.cycle.text.equals(tool.getParseForm().getExt("collectMode"))) {
            headers.put("M_commitCycle", "所属周期");
            sub_headers.put("M_commitCycle", "所属周期");
        }

        headers.put("M_source", "数据来源");

        //企业填报数据
        List<FormRecordListVo> list = queryAllWithBody(query);

        //导出
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        String fileName = URLEncoder.encode(tool.getParseForm().getFormName(), "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        tool.exportAndCloseStream(response.getOutputStream(), list, headers, sub_headers);
    }


    public PageInfo<MdFormRecord> queryByPage(FormRecordQuery query) {
        MdFormRecordExample example = buildEx(query);
        example.setOrderByClause("create_time desc");
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        List<MdFormRecord> list = recordService.selectByExample(example);
        PageInfo<MdFormRecord> pageInfo = new PageInfo<>(list);
        return pageInfo;
    }

    public PageInfo<FormRecordListVo> queryWithBodyByPage(FormRecordQuery query) {
        MdFormRecordExample example = buildEx(query);
        example.setOrderByClause("create_time desc");
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        List<MdFormRecord> list = recordService.selectByExampleWithBLOBs(example);

        PageInfo<MdFormRecord> pageInfo = new PageInfo<>(list);

        if (CollectionUtils.isEmpty(pageInfo.getList())) {
            return ZQTPageUtil.bulid(pageInfo.getList(), new ArrayList<>());
        }

        Map<String, String> userCache = new HashMap<>();
        Map<String, MdForm> formCache = new HashMap<>();

        return ZQTPageUtil.bulid(pageInfo.getList(), list.stream().map(item -> {
            FormRecordListVo vo = new FormRecordListVo();
            BeanUtils.copyProperties(item, vo);

            // ownUser
            if (userCache.containsKey(vo.getOwnUser())) {
                vo.setUserName(userCache.get(vo.getOwnUser()));
            } else {
                SysUser sysUser = sysUserMapper.selectUserById(vo.getOwnUser());
                if (sysUser != null) {
                    vo.setUserName(sysUser.getNickName());
                    userCache.put(vo.getOwnUser(), sysUser.getNickName());
                }
            }

            // form相关
            if (formCache.containsKey(vo.getFormId())) {

                MdForm form = formCache.get(vo.getFormId());
                vo.setFormName(form.getName());
                vo.setFormImg(form.getImg());
                vo.setPublishTime(form.getPublishTime());

                String ownUser = form.getOwnUser();
                if (userCache.containsKey(ownUser)) {
                    vo.setFormOwnUserName(userCache.get(ownUser));
                }
            } else {
                MdForm form = formService.selectByPrimaryKey(vo.getFormId());
                formCache.put(vo.getFormId(), form);
                vo.setFormName(form.getName());
                vo.setFormImg(form.getImg());
                vo.setPublishTime(form.getPublishTime());

                String ownUser = form.getOwnUser();
                if (userCache.containsKey(ownUser)) {
                    vo.setFormOwnUserName(userCache.get(ownUser));
                } else {
                    SysUser sysUser = sysUserMapper.selectUserById(ownUser);
                    if (sysUser != null) {
                        vo.setFormOwnUserName(sysUser.getNickName());
                        userCache.put(ownUser, sysUser.getNickName());
                    }
                }
            }

            if (StringUtils.isBlank(vo.getSource())) {
                vo.setSource("线上填写");
            }
            return vo;
        }).collect(Collectors.toList()));
    }


    public List<FormRecordListVo> queryAllWithBody(FormRecordQuery query) {
        MdFormRecordExample example = buildEx(query);
        example.setOrderByClause("create_time desc");
        List<MdFormRecord> list = recordService.selectByExampleWithBLOBs(example);

        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }

        return list.stream().map(item -> {
            FormRecordListVo vo = new FormRecordListVo();
            BeanUtils.copyProperties(item, vo);

            if (StringUtils.isBlank(vo.getUserName())) {
                vo.setUserName(vo.getCommitName());
            }

            return vo;
        }).collect(Collectors.toList());
    }

    /**
     * 更新填报
     */
    public String save(MdFormRecord body) {
        if (body == null) {
            throw new RuntimeException("表单数据为空");
        }

        if (StringUtils.isBlank(body.getId())) {

            MdForm mdForm = formManager.detail(body.getFormId());
            if (mdForm == null) {
                throw new RuntimeException("表单为空");
            }

            MdFormRecord record = new MdFormRecord();
            record.setFormId(body.getFormId());
            record.setId("record-" + UUID.randomUUID().toString());
            record.setCreateTime(System.currentTimeMillis());
            record.setStatus(1);
            record.setCommitStatus(1);
            record.setOwnUser(body.getOwnUser());
            record.setCommitCycle(mdForm.getCycleNow());
            record.setRecords(body.getRecords());
            record.setSource("线上填写");

            if (recordService.insertSelective(record) > 0) {
                updateRecordNum(record.getFormId());
                return record.getId();
            }

        } else {
            MdFormRecord record = recordService.selectByPrimaryKey(body.getId());

            MdForm mdForm = formManager.detail(record.getFormId());
            if (mdForm == null) {
                throw new RuntimeException("表单为空");
            }

            boolean isUpdate = Integer.valueOf(1).equals(record.getCommitStatus());

            record.setCreateTime(System.currentTimeMillis());
            record.setCommitStatus(1);
            record.setOwnUser(body.getOwnUser());
            record.setRecords(body.getRecords());
            record.setSource("线上填写");

            if (recordService.updateByPrimaryKeySelective(record) > 0) {
                updateRecordNum(record.getFormId());
                return body.getId();
            }
        }

        throw new RuntimeException("保存表单数据失败");
    }


    public boolean deleteRecord(List<String> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return false;
        }

        MdFormRecordExample example = new MdFormRecordExample();
        example.createCriteria().andIdIn(ids);

        List<MdFormRecord> list = recordService.selectByExampleWithBLOBs(example);
        if (CollectionUtil.isEmpty(list)) {
            return false;
        }

        String formId = list.get(0).getFormId();
        updateRecordNum(formId);
        return true;
    }

    private void updateRecordNum(String formId) {
        MdForm form = formManager.detail(formId);

        MdFormRecordExample example = new MdFormRecordExample();
        MdFormRecordExample.Criteria c = example.createCriteria().andFormIdEqualTo(formId).andCommitStatusEqualTo(1);
        if (StringUtils.isNotBlank(form.getCycleNow())) {
            c.andCommitCycleEqualTo(form.getCycleNow());
        }

        int count = recordService.countByExample(example);

        if (form.getNeedRation() != null && form.getNeedRation() == 1
                && form.getRationNum() != null) {
            if (count >= form.getRationNum()) {
                form.setPublishStatus(EnumFormPublishStatus.finish.code);
            }
        }
        form.setRecordNum(count);
        formService.updateByPrimaryKeySelective(form);
    }


    /**
     * 删除未提交的数据
     *
     * @param form_id
     * @param commitCycle
     * @param userIds
     */
    public void deleteNoCommit(String form_id, String commitCycle, List<String> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }

        MdFormRecordExample example = new MdFormRecordExample();
        MdFormRecordExample.Criteria c = example.createCriteria();
        if (StringUtils.isNotBlank(form_id)) {
            c.andFormIdEqualTo(form_id);
        }
        if (StringUtils.isNotBlank(commitCycle)) {
            c.andCommitCycleEqualTo(commitCycle);
        }
        c.andCommitStatusEqualTo(0);
        c.andOwnUserIn(userIds);

        if (recordService.deleteByExample(example) == 0) {
            throw new RuntimeException("发布失败:无法删除填报数据");
        }
    }

    /**
     * 填报详情页
     */
    public FormRecordVo detail(String id) {
        if (StringUtils.isBlank(id)) {
            return null;
        }

        MdFormRecord record = recordService.selectByPrimaryKey(id);
        if (record == null) {
            return null;
        }

        FormRecordVo recordVo = new FormRecordVo();
        BeanUtils.copyProperties(record, recordVo);

        MdForm form = formManager.detail(recordVo.getFormId());
        if (form != null) {
            recordVo.setForm_config(form.getFormConfig());
            recordVo.setName(form.getName());
        }
        //针对周期表单。
        String type = EnumFormType.cycle.text;
        if (type.equals(form.getCollectMode())) {
            Boolean flag = judgeTime(form);
            recordVo.setFlag(flag);
        } else {
            long nowTime = System.currentTimeMillis();
            recordVo.setFlag(form.getFixStart() <= nowTime && nowTime < form.getFixEnd());
        }

        return recordVo;
    }


    public void importTemplate(HttpServletResponse response, String formId) throws IOException {
        EformTool tool = getEformTool(formId);
        String fileName = URLEncoder.encode(tool.getParseForm().getFormName() + "导入模版", "UTF-8") + DateUtil.formatDate(new Date());


        LinkedHashMap<String, String> def = new LinkedHashMap<>();
        def.put("M_companyName", "提交人");
        def.put("M_createTime", "填写时间");

        //导出
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");

        tool.config(toolConfig)
                .exportTemplateAndCloseStream(response.getOutputStream(), def, null);
    }


    //根据维度+限制，判断是否可以填写表单。
    private Boolean judgeTime(MdForm form) {
        Boolean flag = false;
        switch (form.getCycleType()) {
            case "ww":
                int now = com.ruoyi.common.utils.DateUtil.getWeek();//今天星期几？
                if (0 <= (form.getCycleLast() - now)) {
                    flag = true;
                }
                break;
            case "m":
                LocalDate localDate = LocalDate.now();
                int nowDay = localDate.getDayOfMonth();
                if (0 <= (form.getCycleLast() - nowDay)) {
                    flag = true;
                }
                break;
            default:
                //此处相差天数，天数隔离，如07.01至08.21.隔离了31+20=51天。
                // 如果设置的限制天数为51，那么今天是不可以填写的。
                int nowQ = (int) getDayQ();
                if (form.getCycleLast() - nowQ > 0) {
                    flag = true;
                }
                break;
        }
        return flag;
    }

    //获取季度开头到现在的天数差
    private long getDayQ() {
        //当前季度
        int nowQ = com.ruoyi.common.utils.DateUtil.getCurrentQuarter();
        //月 日
        String startDay = EnumStartDateByQua.getValue(nowQ);
        //年
        LocalDate localDate = LocalDate.now();
        //季度开始时间
        String startDate = localDate.getYear() + startDay;
        Date date1 = DateUtil.parse(startDate);
        long betweenDay = DateUtil.between(date1, new Date(), DateUnit.DAY);
        return betweenDay;
    }

    public MdFormRecordExample buildEx(FormRecordQuery query) {
        MdFormRecordExample example = new MdFormRecordExample();
        if (query == null) {
            return example;
        }
        MdFormRecordExample.Criteria c = example.createCriteria();

        //最新数据
        if (query.getLastRecord() != null && query.getLastRecord() == 1) {
            query.setCommit_status(null);
            MdForm mdForm = formManager.detail(query.getForm_id());
            query.setCommit_cycle(mdForm.getCycleNow());
            c.andCreateTimeIsNotNull();
        }

        //从未填写
        if (query.getNoRecord() != null && query.getNoRecord() == 1) {
            query.setCommit_status(null);
            MdForm mdForm = formManager.detail(query.getForm_id());
            query.setCommit_cycle(mdForm.getCycleNow());
//            query.setCommit_cycle(null);
            c.andCreateTimeIsNull();
        }

        if (StringUtils.isNotBlank(query.getForm_id())) {
            c.andFormIdEqualTo(query.getForm_id());
        }

        if (StringUtils.isNotBlank(query.getCommit_cycle())) {
            c.andCommitCycleEqualTo(query.getCommit_cycle());
        }

        if (query.getCommit_status() != null) {
            c.andCommitStatusEqualTo(query.getCommit_status());
        }

        if (StringUtils.isNotBlank(query.getUserId())) {
            c.andOwnUserEqualTo(query.getUserId());
        }

        return example;
    }

}
