package ink.wgink.module.form.service.design.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.exceptions.base.SystemException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.form.consts.IFormDesignConst;
import ink.wgink.module.form.enums.design.FormFieldTypeEnum;
import ink.wgink.module.form.enums.design.FormStatusEnum;
import ink.wgink.module.form.enums.design.FormTypeEnum;
import ink.wgink.module.form.pojo.pos.design.FormPO;
import ink.wgink.module.form.pojo.vos.design.FormDesignVO;
import ink.wgink.module.form.pojo.vos.design.FormFieldVO;
import ink.wgink.module.form.pojo.vos.design.FormVO;
import ink.wgink.module.form.service.design.IFormDesignService;
import ink.wgink.module.form.service.design.IFormFieldService;
import ink.wgink.module.form.service.design.IFormService;
import ink.wgink.module.form.service.report.IFormReportService;
import ink.wgink.util.HtmlHelper;
import ink.wgink.util.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Description: 表单设计
 * @Author: WenG
 * @Date: 2022/3/7 10:53
 * @Version: 1.0
 **/
@Service
public class FormDesignServiceImpl extends DefaultBaseService implements IFormDesignService {

    @Autowired
    private IFormService formService;
    @Autowired
    private IFormFieldService formFieldService;
    @Autowired
    private IFormReportService formReportService;

    private Configuration configuration = new Configuration(Configuration.getVersion());

    @PostConstruct
    private void init() {
        configuration.setClassForTemplateLoading(FormDesignServiceImpl.class, "/templates/ftl");
    }

    @Override
    public void save(FormDesignVO formDesignVO) throws IOException {
        List<FormFieldVO> formFields = new ArrayList<>();
        setFormFieldList(formFields, formDesignVO.getData());
        if (formFields.isEmpty()) {
            throw new ParamsException("表单字段不能为空");
        }
        // 处理逻辑：判断原表中是否有数据，如果有，新增一张表，版本号递增；如果没有，删除本地表与关联数据，新建一张表
        String formCode = formDesignVO.getFormCode();
        Integer version = 1;
        String formId = null;
        boolean createNewTable = true;
        // 时间戳表单编码
        if (StringUtils.isBlank(formCode)) {
            formCode = DateUtil.getSdfTimes();
        } else {
            // 判断表中是否有数据，有数据就创建新版本
            Integer latestVersionByCode = formService.getLatestVersionByCode(formCode);
            // 判断表是否存在
            FormPO formPO = formService.getPOByCodeAndVersion(formCode, latestVersionByCode);
            Integer dynamicCount = formService.countDynamicTable(formPO.getFormTableName());
            if (dynamicCount > 0) {
                Integer count = formReportService.count(formPO.getFormTableName());
                if (count == 0) {
                    createNewTable = false;
                    formId = formPO.getFormId();
                    // 删列
                    formFieldService.deleteByFormIds(Arrays.asList(formPO.getFormId()));
                    // 删表
                    formService.deleteTable(formPO.getFormTableName(), false);
                    version = latestVersionByCode;
                } else {
                    version = latestVersionByCode + 1;
                }
            } else {
                LOG.debug("动态表不存在");
                createNewTable = false;
                version = latestVersionByCode;
                formId = formPO.getFormId();
                // 删列
                formFieldService.deleteByFormIds(Arrays.asList(formPO.getFormId()));
            }
        }

        // df：dynamicForm 动态表单
        String tableName = "df_" + formCode + "_v" + version;

        LOG.debug("保存表单");
        FormVO formVO = new FormVO();
        formVO.setFormName(formDesignVO.getFormName());
        formVO.setFormCode(formCode);
        formVO.setFormSummary(formDesignVO.getFormSummary());
        formVO.setFormTableName(tableName);
        formVO.setFormType(formDesignVO.getFormType());
        formVO.setFormStatus(FormStatusEnum.ACTIVE.getValue());
        formVO.setFormVersion(version);
        formVO.setFormSourceData(JSON.toJSONString(formDesignVO.getData()));
        formVO.setMainTitleTpl(formDesignVO.getMainTitleTpl());

        // 美化代码
        for (FormDesignVO.Field field : formDesignVO.getFields()) {
            String fieldCode = HtmlHelper.formatHtml(field.getHtml(), "\t", 4);
            field.setHtml(fieldCode);
        }

        Map<String, Object> model = getHashMap(4);
        model.put("fields", formDesignVO.getFields());

        formVO.setSavePageCode(getSavePageCode(model, formDesignVO.getFormType()));
        formVO.setUpdatePageCode(getUpdatePageCode(model, formDesignVO.getFormType()));
        formVO.setShowPageCode(getShowPageCode(model, formDesignVO.getFormType()));
        formVO.setPrintPageCode(getPrintPageCode(model, formDesignVO.getFormType()));
        formVO.setAppSavePageCode(getAppSavePageCode(model, formDesignVO.getFormType()));
        formVO.setAppUpdatePageCode(getAppUpdatePageCode(model, formDesignVO.getFormType()));
        formVO.setAppShowPageCode(getAppShowPageCode(model, formDesignVO.getFormType()));

        if (createNewTable) {
            formId = formService.saveReturnId(formVO);
        } else {
            formService.update(formId, formVO);
        }

        LOG.debug("保存表单字段");
        for (FormFieldVO formFieldVO : formFields) {
            formFieldVO.setFormId(formId);
            formFieldService.save(formFieldVO);
        }

        LOG.debug("建表");
        String createTableSQL = getCreateTableSQL(tableName, formDesignVO.getFormName(), formFields);
        formService.createDynamicForm(createTableSQL);
    }

    /**
     * 新增页面模板
     *
     * @param model
     * @return
     * @throws IOException
     */
    private String getSavePageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/web/form-save.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/web/oa/form-save.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * 更新页面模板
     *
     * @param model
     * @param formType
     * @return
     * @throws IOException
     */
    private String getUpdatePageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/web/form-update.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/web/oa/form-update.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * 展示页面模板
     *
     * @param model
     * @param formType
     * @return
     * @throws IOException
     */
    private String getShowPageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/web/form-show.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/web/oa/form-show.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * 打印页面模板
     *
     * @param model
     * @param formType
     * @return
     * @throws IOException
     */
    private String getPrintPageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return null;
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/web/oa/form-print.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * app新增页面模板
     *
     * @param model
     * @return
     * @throws IOException
     */
    private String getAppSavePageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/app/app-form-save.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/app/oa/app-form-save.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * app更新页面模板
     *
     * @param model
     * @param formType
     * @return
     * @throws IOException
     */
    private String getAppUpdatePageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/app/app-form-update.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/app/oa/app-form-update.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * app展示页面模板
     *
     * @param model
     * @param formType
     * @return
     * @throws IOException
     */
    private String getAppShowPageCode(Map<String, Object> model, String formType) throws IOException {
        if (StringUtils.equals(FormTypeEnum.DEFAULT.getValue(), formType)) {
            return getTemplateCode("/page/app/app-form-show.ftl", model);
        } else if (StringUtils.equals(FormTypeEnum.OA.getValue(), formType)) {
            return getTemplateCode("/page/app/oa/app-form-show.ftl", model);
        }
        throw new ParamsException("表单类型错误");
    }

    /**
     * 建表SQL
     *
     * @param tableName
     * @param formFieldVOs
     * @return
     */
    private String getCreateTableSQL(String tableName, String formName, List<FormFieldVO> formFieldVOs) {
        StringBuffer createTableSB = new StringBuffer("CREATE TABLE `").append(tableName).append("`(");
        createTableSB.append("`" + IFormDesignService.FIELD_ID + "` bigint(20) unsigned NOT NULL AUTO_INCREMENT,");
        createTableSB.append("`" + IFormDesignService.FIELD_UID + "` char(36) NOT NULL,");
        createTableSB.append("`" + IFormDesignService.PROCESS_INSTANCE_ID + "` char(36) DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_MAIN_TITLE + "` varchar(500),");
        for (FormFieldVO formFieldVO : formFieldVOs) {
            createTableSB.append("`").append(formFieldVO.getFieldName()).append("` ").append(formFieldVO.getFieldType()).append(" DEFAULT NULL COMMENT '").append(formFieldVO.getFieldExplain()).append("',");
        }
        createTableSB.append("`" + IFormDesignService.FIELD_ATTACHMENTS + "` text DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_CREATOR + "` char(36) DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_GMT_CREATE + "` datetime DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_MODIFIER + "` char(36) DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_GMT_MODIFIED + "` datetime DEFAULT NULL,");
        createTableSB.append("`" + IFormDesignService.FIELD_IS_DELETE + "` int(2) DEFAULT '0',");
        createTableSB.append("PRIMARY KEY (`" + IFormDesignService.FIELD_ID + "`)");
        createTableSB.append(") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='").append(formName).append("';");
        return createTableSB.toString();
    }

    /**
     * 设置表单字段列表
     *
     * @param formFields
     * @param dataJsonArray
     */
    private void setFormFieldList(List<FormFieldVO> formFields, JSONArray dataJsonArray) {
        formFields = formFields == null ? new ArrayList<>() : formFields;
        for (int i = 0; i < dataJsonArray.size(); i++) {
            JSONObject dataJsonObject = dataJsonArray.getJSONObject(i);
            String id = dataJsonObject.getString(IFormDesignConst.ID);
            String index = dataJsonObject.getString(IFormDesignConst.INDEX);
            String label = dataJsonObject.getString(IFormDesignConst.LABEL);
            String tag = dataJsonObject.getString(IFormDesignConst.TAG);
            String listShow = dataJsonObject.getString(IFormDesignConst.LIST_SHOW);
            // grid特殊，需要递归处理
            if (StringUtils.equals(FormFieldTypeEnum.GRID.getType(), tag)) {
                JSONArray columnJsonArray = dataJsonObject.getJSONArray(IFormDesignConst.COLUMNS);
                for (int j = 0; j < columnJsonArray.size(); j++) {
                    JSONObject columnJsonObject = columnJsonArray.getJSONObject(j);
                    JSONArray listJsonArray = columnJsonObject.getJSONArray(IFormDesignConst.LIST);
                    if (!listJsonArray.isEmpty()) {
                        setFormFieldList(formFields, listJsonArray);
                    }
                }
            } else {
                FormFieldVO formField = new FormFieldVO();
                // 文号，加后后缀保存
                if (StringUtils.equals(FormFieldTypeEnum.DOC_NO.getType(), tag)) {
                    formField.setFieldName(id + SUFFIX_DOC_NO);
                } else {
                    formField.setFieldName(id);
                }
                formField.setFieldTag(tag);
                formField.setFieldExplain(label);
                formField.setFieldType(getSingleFieldType(tag));
                formField.setListShow(listShow);
                formFields.add(formField);
            }
        }
    }

    private String getSingleFieldType(String fieldTag) {
        if (StringUtils.equals(FormFieldTypeEnum.INPUT.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.NUMBER_INPUT.getType(), fieldTag)) {
            return "DOUBLE(11,4)";
        } else if (StringUtils.equals(FormFieldTypeEnum.PASSWORD.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.RADIO.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.RADIO_DICTIONARY.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.SELECT.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.SELECT_DICTIONARY.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.CHECKBOX.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.CHECKBOX_DICTIONARY.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.TEXTAREA.getType(), fieldTag)) {
            return "TEXT";
        } else if (StringUtils.equals(FormFieldTypeEnum.CAROUSEL.getType(), fieldTag)) {
            return "VARCHAR(300)";
        } else if (StringUtils.equals(FormFieldTypeEnum.UPLOAD_FILE.getType(), fieldTag)) {
            return "VARCHAR(1000)";
        } else if (StringUtils.equals(FormFieldTypeEnum.UPLOAD_IMAGE.getType(), fieldTag)) {
            return "VARCHAR(300)";
        } else if (StringUtils.equals(FormFieldTypeEnum.UPLOAD_VIDEO.getType(), fieldTag)) {
            return "VARCHAR(300)";
        } else if (StringUtils.equals(FormFieldTypeEnum.UPLOAD_AUDIO.getType(), fieldTag)) {
            return "VARCHAR(300)";
        } else if (StringUtils.equals(FormFieldTypeEnum.RATE.getType(), fieldTag)) {
            return "DOUBLE(11,2)";
        } else if (StringUtils.equals(FormFieldTypeEnum.DATE.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.SLIDER.getType(), fieldTag)) {
            return "DOUBLE(11,2)";
        } else if (StringUtils.equals(FormFieldTypeEnum.SWITCH.getType(), fieldTag)) {
            return "INT(1)";
        } else if (StringUtils.equals(FormFieldTypeEnum.LABEL_GENERATION.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.EDITOR.getType(), fieldTag)) {
            return "LONGTEXT";
        } else if (StringUtils.equals(FormFieldTypeEnum.SIGN.getType(), fieldTag)) {
            return "VARCHAR(255)";
        } else if (StringUtils.equals(FormFieldTypeEnum.DOC_NO.getType(), fieldTag)) {
            return "VARCHAR(255)";
        }
        return null;
    }

    /**
     * 获取模板code
     *
     * @param fileName
     * @param model
     * @return
     * @throws IOException
     */
    private String getTemplateCode(String fileName, Map<String, Object> model) throws IOException {
        Template template = getTemplate(fileName);
        StringWriter stringWriter = new StringWriter();
        try {
            template.process(model, stringWriter);
        } catch (TemplateException e) {
            LOG.error(e.getMessage(), e);
            throw new SystemException("模板错误");
        }
        return stringWriter.toString();
    }

    private Template getTemplate(String fileName) throws IOException {
        return configuration.getTemplate(fileName, ISystemConstant.CHARSET_UTF8);
    }

}
