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

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.module.form.dao.formreport.IFormReportDao;
import ink.wgink.module.form.enums.design.FormStatusEnum;
import ink.wgink.module.form.pojo.pos.design.FormPO;
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.pojo.ListPage;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.util.UUIDUtil;
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 java.sql.Timestamp;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @ClassName: FormReportServiceImpl
 * @Description: 表单上报
 * @Author: wanggeng
 * @Date: 2022/3/14 5:30 PM
 * @Version: 1.0
 */
@Service
public class FormReportServiceImpl extends DefaultBaseService implements IFormReportService {

    @Autowired
    private IFormReportDao formReportDao;
    @Autowired
    private IFormService formService;
    @Autowired
    private IFormFieldService formFieldService;
    private static Pattern MAIN_TITLE_CODE = Pattern.compile("\\$\\{\\w+\\}");

    @Override
    public void save(String formCode, Integer formVersion, Map<String, Object> params) {
        saveAndReturnId(formCode, formVersion, params);
    }

    @Override
    public void saveByToken(String token, String formCode, Integer formVersion, Map<String, Object> params) {
        saveByTokenAndReturnId(token, formCode, formVersion, params);
    }

    @Override
    public void saveByUserId(String userId, String formCode, Integer formVersion, Map<String, Object> params) {
        saveByUserIdAndReturnId(userId, formCode, formVersion, params);
    }

    @Override
    public String saveAndReturnId(String formCode, Integer formVersion, Map<String, Object> params) {
        return saveByUserIdAndReturnId(securityComponent.getCurrentUser().getUserId(), formCode, formVersion, params);
    }

    @Override
    public String saveByTokenAndReturnId(String token, String formCode, Integer formVersion, Map<String, Object> params) {
        String userId = getAppTokenUser(token).getId();
        return saveByUserIdAndReturnId(userId, formCode, formVersion, params);
    }

    @Override
    public String saveByUserIdAndReturnId(String userId, String formCode, Integer formVersion, Map<String, Object> params) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);
        String uid = UUIDUtil.getUUID();
        List<String> inserts = new ArrayList<>();
        List<Object> values = new ArrayList<>();
        inserts.add(IFormDesignService.FIELD_UID);
        values.add(uid);

        // 设置主标题，并更新到params，保存到流程中
        inserts.add(IFormDesignService.FIELD_MAIN_TITLE);
        String mainTitle = getMainTitleCode(formPO.getMainTitleTpl(), params);
        values.add(mainTitle);

        for (Map.Entry<String, Object> kv : params.entrySet()) {
            inserts.add(kv.getKey());
            values.add(kv.getValue());
        }
        String datetime = DateUtil.getTime();
        inserts.add(IFormDesignService.FIELD_GMT_CREATE);
        values.add(datetime);
        inserts.add(IFormDesignService.FIELD_CREATOR);
        values.add(userId);
        inserts.add(IFormDesignService.FIELD_GMT_MODIFIED);
        values.add(datetime);
        inserts.add(IFormDesignService.FIELD_MODIFIER);
        values.add(userId);
        inserts.add(IFormDesignService.FIELD_IS_DELETE);
        values.add(0);

        Map<String, Object> insertParams = getHashMap((params.size() + 5) * 2);
        insertParams.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        insertParams.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        insertParams.put(IFormDesignService.PARAM_INSERTS, inserts);
        insertParams.put(IFormDesignService.PARAM_VALUES, values);
        params.put(IFormDesignService.FIELD_MAIN_TITLE, mainTitle);

        formReportDao.save(insertParams);
        return uid;
    }

    @Override
    public void remove(String formCode, Integer formVersion, List<String> uids) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);

        Map<String, Object> params = getHashMap(10);
        params.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        params.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        params.put(IFormDesignService.FIELD_UIDS, uids);
        setUpdateInfo(params);

        formReportDao.remove(params);
    }

    @Override
    public void update(String formCode, Integer formVersion, String uid, Map<String, Object> params) {
        String userId = securityComponent.getCurrentUser().getUserId();
        updateByUserId(userId, formCode, formVersion, uid, params);
    }

    @Override
    public void updateByToken(String token, String formCode, Integer formVersion, String uid, Map<String, Object> params) {
        String userId = getAppTokenUser(token).getId();
        updateByUserId(userId, formCode, formVersion, uid, params);
    }

    @Override
    public void updateByUserId(String userId, String formCode, Integer formVersion, String uid, Map<String, Object> params) {
        updateByCreatorAndModifier(null, userId, formCode, formVersion, uid, params);
    }

    @Override
    public void updateByCreatorAndModifier(String creator, String modifier, String formCode, Integer formVersion, String uid, Map<String, Object> params) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);

        List<List<String>> kvList = new ArrayList<>();
        for (Map.Entry<String, Object> kv : params.entrySet()) {
            if (kv.getValue() == null) {
                continue;
            }
            kvList.add(Arrays.asList(new String[]{kv.getKey(), "" + kv.getValue()}));
        }
        kvList.add(Arrays.asList(new String[]{IFormDesignService.FIELD_GMT_MODIFIED, DateUtil.getTime()}));
        kvList.add(Arrays.asList(new String[]{IFormDesignService.FIELD_MODIFIER, modifier}));

        Map<String, Object> updateParams = getHashMap((params.size() + 3) * 2);
        updateParams.put(IFormDesignService.FIELD_UID, uid);
        updateParams.put(IFormDesignService.PARAM_KV_LIST, kvList);
        updateParams.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        updateParams.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        updateParams.put(IFormDesignService.FIELD_CREATOR, creator);

        formReportDao.update(updateParams);
        // 更新mainTitle
        Map<String, Object> dataMap = get(formCode, formVersion, uid);
        String mainTitle = getMainTitleCode(formPO.getMainTitleTpl(), dataMap);
        kvList.clear();
        kvList.add(Arrays.asList(new String[]{IFormDesignService.FIELD_MAIN_TITLE, mainTitle}));
        formReportDao.update(updateParams);

        // 将mainTitle添加到params中，保存到流程中
        params.put(IFormDesignService.FIELD_MAIN_TITLE, mainTitle);
    }

    @Override
    public void updateProcessInstanceId(String formCode, Integer formVersion, String uid, String processInstanceId) {
        Map<String, Object> params = getHashMap(8);
        params.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        params.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        params.put(IFormDesignService.FIELD_UID, uid);
        params.put(IFormDesignService.PARAM_PROCESS_INSTANCE_ID, processInstanceId);

        formReportDao.updateProcessInstanceId(params);
    }

    @Override
    public Map<String, Object> get(String formCode, Integer formVersion, String uid) {
        Map<String, Object> params = getHashMap(2);
        params.put(IFormDesignService.FIELD_UID, uid);
        params.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        params.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        return formReportDao.get(params);
    }

    @Override
    public Map<String, Object> getByUserId(String userId, String formCode, Integer formVersion, String uid) {
        Map<String, Object> params = getHashMap(2);
        params.put(IFormDesignService.FIELD_UID, uid);
        params.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        params.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        params.put(IFormDesignService.FIELD_CREATOR, userId);
        return formReportDao.get(params);
    }

    @Override
    public List<Map<String, Object>> list(String formCode, Integer formVersion, Map<String, Object> params) {
        return listByUserId(null, formCode, formVersion, params);
    }

    @Override
    public List<Map<String, Object>> listByUserId(String userId, String formCode, Integer formVersion, Map<String, Object> params) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);

        List<String> queryList = conditionList(params);
        Map<String, Object> queryParams = getHashMap(6);
        queryParams.put(IFormDesignService.PARAM_FORM_CODE, formCode);
        queryParams.put(IFormDesignService.PARAM_FORM_VERSION, formVersion);
        queryParams.put(IFormDesignService.PARAM_QUERY_LIST, queryList);
        queryParams.put(IFormDesignService.PARAM_LIST_SHOW_FIELD, listShowField(formPO));
        queryParams.put(IFormDesignService.FIELD_CREATOR, userId);

        return formReportDao.list(queryParams);
    }

    @Override
    public SuccessResultList<List<Map<String, Object>>> listPage(String formCode, Integer formVersion, ListPage page) {
        return listPageByUserId(null, formCode, formVersion, page);
    }

    @Override
    public SuccessResultList<List<Map<String, Object>>> listPageByUserId(String userId, String formCode, Integer formVersion, ListPage page) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);
        List<String> queryList = conditionList(page.getParams());
        Map<String, Object> queryParams = getListQueryParams(formPO, queryList);
        queryParams.put(IFormDesignService.PARAM_LIST_SHOW_FIELD, listShowField(formPO));
        queryParams.put(IFormDesignService.FIELD_CREATOR, userId);
        return listPage(page, queryParams);
    }

    @Override
    public SuccessResultList<List<Map<String, Object>>> listPageAllField(String formCode, Integer formVersion, ListPage page) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);
        List<String> queryList = conditionList(page.getParams());
        Map<String, Object> queryParams = getListQueryParams(formPO, queryList);
        return listPage(page, queryParams);
    }

    private SuccessResultList<List<Map<String, Object>>> listPage(ListPage page, Map<String, Object> queryParams) {
        PageHelper.startPage(page.getPage(), page.getRows());
        List<Map<String, Object>> mapList = formReportDao.list(queryParams);
        mapList.forEach(map -> {
            if (map.containsKey(IFormDesignService.FIELD_GMT_CREATE) && map.get(IFormDesignService.FIELD_GMT_CREATE) != null) {
                map.put(IFormDesignService.FIELD_GMT_CREATE, DateUtil.getDateTime(new Date(((Timestamp) map.get(IFormDesignService.FIELD_GMT_CREATE)).getTime())));
            }
            if (map.containsKey(IFormDesignService.FIELD_GMT_MODIFIED) && map.get(IFormDesignService.FIELD_GMT_MODIFIED) != null) {
                map.put(IFormDesignService.FIELD_GMT_MODIFIED, DateUtil.getDateTime(new Date(((Timestamp) map.get(IFormDesignService.FIELD_GMT_MODIFIED)).getTime())));
            }
        });
        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>(mapList);
        return new SuccessResultList<>(mapList, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    /**
     * 获取列表查询参数
     *
     * @param formCode
     * @param formVersion
     * @param queryList
     * @return
     */
    private Map<String, Object> getListQueryParams(FormPO formPO, List<String> queryList) {
        Map<String, Object> queryParams = getHashMap(6);
        queryParams.put(IFormDesignService.PARAM_FORM_CODE, formPO.getFormCode());
        queryParams.put(IFormDesignService.PARAM_FORM_VERSION, formPO.getFormVersion());
        queryParams.put(IFormDesignService.PARAM_QUERY_LIST, queryList);
        return queryParams;
    }

    @Override
    public List<String> listShowField(String formCode, Integer formVersion) {
        FormPO formPO = formService.getPOByCodeAndVersion(formCode, formVersion);
        checkForm(formPO);
        return listShowField(formPO);
    }

    @Override
    public Integer count(String tableName) {
        Integer count = formReportDao.countByTableName(tableName);
        return count == null ? 0 : count;
    }

    /**
     * 校验表单
     *
     * @param formPO
     */
    private void checkForm(FormPO formPO) {
        if (formPO == null) {
            throw new SearchException("表单不存在");
        }
        if (!StringUtils.equals(FormStatusEnum.ACTIVE.getValue(), formPO.getFormStatus())) {
            throw new SearchException("表单未激活");
        }
    }

    /**
     * 显示字段
     *
     * @param formPO
     * @return
     */
    private List<String> listShowField(FormPO formPO) {
        return formFieldService.listFieldNameByFormIdAndListShow(formPO.getFormId(), true);
    }

    /**
     * 设置主标题
     *
     * @param mainTitleTpl
     * @param map
     */
    private String getMainTitleCode(String mainTitleTpl, Map<String, Object> map) {
        if (StringUtils.isBlank(mainTitleTpl)) {
            return "";
        }
        String mainTitle = mainTitleTpl;
        Matcher matcher = MAIN_TITLE_CODE.matcher(mainTitle);
        while (matcher.find()) {
            String tpl = matcher.group();
            String key = tpl.substring(2, tpl.length() - 1);
            Object valueObj = map.get(key);
            if (valueObj == null) {
                mainTitle = StringUtils.replace(mainTitle, tpl, "");
            } else {
                mainTitle = StringUtils.replace(mainTitle, tpl, valueObj.toString());
            }
        }
        return mainTitle;
    }

    /**
     * 条件列表
     *
     * @param params
     * @return
     */
    private List<String> conditionList(Map<String, Object> params) {
        // 清除条件中的默认字段
        params.remove("requestUri");
        params.remove("requestHost");
        params.remove("requestPort");
        params.remove("keywords");
        params.remove("showLevel");
        params.remove("page");
        params.remove("rows");
        params.remove("is_delete");

        List<String> queryList = new ArrayList<>();
        for (Map.Entry<String, Object> kv : params.entrySet()) {
            String condition = getCondition(kv.getKey(), kv.getValue());
            if (StringUtils.isBlank(condition)) {
                continue;
            }
            queryList.add(condition);
        }
        return queryList;
    }

    private String getCondition(String key, Object value) {
        String conditionSQL;
        if (value == null) {
            return null;
        }
        String valueStr = "" + value;
        if (StringUtils.isBlank(valueStr)) {
            return null;
        }
        if (StringUtils.startsWith(valueStr, "*") && StringUtils.endsWith(valueStr, "*")) {
            // key LIKE '%keyword%'
            conditionSQL = key + " LIKE '%" + valueStr.substring(1, valueStr.length() - 1) + "%'";
        } else if (StringUtils.startsWith(valueStr, "*")) {
            // key LIKE '%keyword'
            conditionSQL = key + " LIKE '%" + valueStr.substring(1) + "'";
        } else if (StringUtils.endsWith(valueStr, "*")) {
            // key LIKE 'keyword%'
            conditionSQL = key + " LIKE '" + valueStr.substring(0, valueStr.length() - 1) + "%'";
        } else if (StringUtils.startsWith(valueStr, "lt;")) {
            // key < 'value'
            conditionSQL = key + " < '" + valueStr.substring(3) + "'";
        } else if (StringUtils.endsWith(valueStr, "gt;")) {
            // key > 'value'
            conditionSQL = key + " > '" + valueStr.substring(3) + "'";
        } else if (StringUtils.startsWith(valueStr, "le;")) {
            // key <= 'value'
            conditionSQL = key + " <= '" + valueStr.substring(3) + "'";
        } else if (StringUtils.endsWith(valueStr, "ge;")) {
            // key >= 'value'
            conditionSQL = key + " >= '" + valueStr.substring(3) + "'";
        } else if (StringUtils.endsWith(valueStr, "ne;")) {
            conditionSQL = key + " <> '" + valueStr.substring(3) + "'";
        } else {
            conditionSQL = key + " = '" + valueStr + "'";
        }
        return conditionSQL;
    }
}
