package com.malk.mcli.controller;


import com.alibaba.fastjson.JSONObject;
import com.malk.server.aliwork.YDConf;
import com.malk.server.aliwork.YDParam;
import com.malk.server.common.McException;
import com.malk.server.common.McR;
import com.malk.service.aliwork.YDClient;
import com.malk.utils.UtilMap;
import com.malk.utils.UtilServlet;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

/**
 * 错误抛出与拦截详见CatchException
 */
@Slf4j
@RestController
@RequestMapping("/matrix")
public class ApprovalMatrixController {

    /**
     * 审批矩阵验证说明
     * 1. 审批预览
     * - 审批预览会调用矩阵接口, 预览开启若存在超时情况不能发起
     * - 预览若是节点报错, 则可以正常发起, 预览时该节点不会显示 [ 若是通过预览实现强管控, 可在异常内添加超时机制, 控制不能发起 ]
     * 2. 超时情况
     * - 目前官方给到默认 timeout 5s, 且不支持配置
     * - 审批流程会等待全部节点请求返回后才会显示, 数据提交后若某一个节点超时, 则该节点审批人不显示 [ 可搭配缓存逻辑, 关闭提交预览 ]
     * 3. 抛出异常
     * - 触发: 1. 节点返回为空, 且未配置为空自动通过; 2. 节点抛出异常
     * - 若是第一个节点就异常, 管理员可在管理后台, 数据列表页面点击重试按钮进行重新激活
     * - 若是中途节点, 该节点审批会发挥接口抛出异常信息, 若是返回空切设置为空不自动通过, 则会抛出审批人不能为空异常提醒, 流程点击同意都不能向下流转
     * 4. 缓存逻辑
     * - 原理: 缓存机制基于唯一ID, 自定义或数据实例Id, 否则额若传递矩阵参数会增加矩阵配置复杂度\降低其灵活性
     * - 1. 若是节点可保障在 timeout 前返回, 可基于自定义 ID 进行数据缓存, 通过预览触发缓存, 节点异常通过超时控制不能发起, 同时解决发起异常问题
     * - 2. 若节点存在超时情况多, 且可忽略预览效果情况下, 可通过数据实例ID缓存, 首次打开可能显示不全, 缓存成功后直接加载
     * - 兜底: 缓存成功先返回缓存数据, 后台持续执行更新逻辑, 兼容条件\人员矩阵变更, 未到达审批节点更新. 若是存在退回场景, 可触发新的审批人员修正
     * 5. 待办推送
     * - 第一个节点异常重新激活的流程, 待办会正常触发推送
     * - 未执行到节点, 若人员变更, 会在执行到该节点时触发待办推送 [ 目前转交有api可调用 ]
     * 6. 补充说明
     * - 已跳过/审批中节点, 再次打开不会触发调用, 未触发的节点, 每次页面打开都会触发调用 [ 缓存可很好降低并发问题 ]
     * - 若是设置了为空自动通过, 流程会自动忽略该节点, 自动到下一个节点. 已跳过/审批中节点, 再次打开不会触发调用, 通过退回到该节点前可重新激活
     * 7. 功能优化
     * - 目前采用缓存方案2, 后续可考虑切换缓存方案1, 若是存在超时场景, 需要对组织架构\矩阵数据缓存, 解决QPS导致超时问题
     * - 矩阵审批节点全部配置为空不允许自动通过 (默认配置), 是否为空可配置矩阵逻辑内, 避免审批节点丢失 [ 实现逻辑: 若审批可为空, 且该节点为空, 返回系统审批账号, 再调用自动同意 ]
     */
    @SneakyThrows
    @PostMapping("approval")
    List<String> approvalMatrix(HttpServletRequest request) {

        Map data = UtilServlet.getParamMap(request);
        log.info("审批矩阵, {}", data);

        McException.assertAccessException("是".equals(UtilMap.getString(data, "exception")), "抛出异常");

        if (!"是".equals(UtilMap.getString(data, "cache_r"))) {
            int s = UtilMap.getInt(data, "sleep_s");
            if (s > 0) {
                Thread.sleep(s * 1000);
            }
        }
        return (List<String>) JSONObject.parse(String.valueOf(JSONObject.parse(UtilMap.getString(data, "employeeField_"))));
    }


    private static final String appType_Tech = "APP_H3A35LRTTIQZE14NMCIA";
    private static final String systemToken_Tech = "1IC66671L7UO3SA58DBNM6269FP12LNO9YE1MHA1";

    @Autowired
    private YDClient ydClient;

    /**
     * 审批记录接口，会返回未执行的节点. 同时返回 type \ actionExit, 可用于区分 历史 \ 进行中 \ 未到达 状态
     */
    @GetMapping("record")
    McR approvalRecord(@RequestParam String processInstanceId) {

        log.info("审批记录, {}", processInstanceId);

        List<Map> records = (List<Map>) ydClient.queryData(YDParam.builder()
                .appType(appType_Tech)
                .systemToken(systemToken_Tech)
                .processInstanceId(processInstanceId)
                .build(), YDConf.FORM_QUERY.retrieve_approval_record).getResult();
        return McR.success(records);
    }

    /**
     * 自动化、编辑事件则是只要更新就会执行。通过api 则需要传递更新执行参数，才会触发业务规则
     */

}
