package com.huigou.bpm.management.controller;

import com.huigou.bpm.management.application.ProcInstApplication;
import com.huigou.bpm.management.application.ProcessDeploymentApplication;
import com.huigou.bpm.management.domain.model.ProcInst;
import com.huigou.bpm.management.domain.model.ProcessDeployment;
import com.huigou.bpm.management.domain.query.ProcInstQueryRequest;
import com.huigou.data.domain.model.CommonDomainConstants;
import com.huigou.exception.ApplicationException;
import com.huigou.uasp.annotation.ControllerMapping;
import com.huigou.uasp.client.CommonController;
import com.huigou.util.SDO;
import com.huigou.util.StringUtil;
import com.mxgraph.io.mxCodec;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.util.mxXmlUtils;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricVariableInstance;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.w3c.dom.Document;

import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 流程实例管理。
 *
 * @author yonghuan
 */
@ControllerMapping("/bpm/procInst")
@Controller
public class ProcInstController extends CommonController {

    private final Pattern FORM_URL_PATTERN = Pattern.compile("\\$\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}");
    private final static Pattern EXTERNAL_URL_PATTERN = Pattern.compile("^https?://.+");
    @Autowired
    private ProcInstApplication procInstApplication;
    @Autowired
    private ProcessDeploymentApplication processDeploymentApplication;
    @Autowired
    private HistoryService historyService;

    public String forwardList() {
        return forward("procInstList");
    }

    public String sliceQuery(ProcInstQueryRequest queryRequest) {
        return toResult(procInstApplication.sliceQuery(queryRequest));
    }

    public String forwardViewer(SDO sdo) throws Exception {
        Assert.hasText(sdo.getId(), CommonDomainConstants.ID_NOT_BLANK);
        ProcInst procInst = procInstApplication.findById(sdo.getId());
        String xml = procInstApplication.findProcInstXml(sdo.getId());
        putAttribute("xml", xml);
        ProcessDeployment pd = processDeploymentApplication.findById(procInst.getProcDeployId());
        putAttribute("procdefId", pd.getProcdefId());
        putAttribute("isReadOnly", true);
        return forward("/system/tbpm/editor/viewer.jsp", procInst);
    }

    /**
     * 跳转到流程节点得表单页面
     */
    public String forwardForm(SDO sdo) throws Exception {
        String id = sdo.getId();
        Assert.hasText(id, CommonDomainConstants.ID_NOT_BLANK);
        String activityCode = sdo.getString("activityCode");
        Assert.hasText(activityCode, "流程节点的code不能为空");
        ProcInst procInst = procInstApplication.findById(id);
        Assert.notNull(procInst, "流程实例不存在");
        ProcessDeployment deployment = processDeploymentApplication.findById(procInst.getProcDeployId());
        Document document = mxXmlUtils.parseXml(deployment.getXml());
        mxCodec codec = new mxCodec(document);
        mxGraphModel graphModel = (mxGraphModel) codec.decode(document.getDocumentElement());
        return graphModel.getCells().values()
                .stream()
                .map(mxCell.class::cast)
                .filter(cell -> activityCode.equals(cell.getAttribute("code")))
                .map(cell -> StringUtils.trim(cell.getAttribute("formUrl")))
                .filter(StringUtils::isNotBlank)
                // TODO 这里考虑用解释器模式重构
                .map(formUrl -> parseFormUrl(procInst, formUrl))
                .map(formUrl -> String.join(":", "redirect", formUrl))
                .findAny()
                .orElseThrow(() -> new IllegalArgumentException(String.format("流程实例不存在 %s 节点", activityCode)));
    }

    /**
     * 替换掉formUrl里面的参数。
     *
     * @throws ApplicationException
     */
    private String parseFormUrl(ProcInst procInst, String formUrl) {
        Map<String, Object> variables = historyService.createHistoricVariableInstanceQuery().processInstanceId(procInst.getId()).list()
                .stream()
                .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));
        OgnlContext context = new OgnlContext(variables);
        StringUtil.replaceAll(FORM_URL_PATTERN, formUrl, matcher -> {
            String variableName = matcher.group(1);
            try {
                return String.valueOf(Ognl.getValue(variableName, context));
            } catch (OgnlException e) {
                throw new ApplicationException("解析表单url出错", e);
            }
        });
        return formUrl;
    }

    @Override
    protected String getPagePath() {
        return "/system/tbpm/procInst/";
    }
}
