package com.casic.missiles.controller.system;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.casic.missiles.core.base.controller.ExportController;
import com.casic.missiles.core.page.PageFactory;
import com.casic.missiles.core.page.PageInfoBT;
import com.casic.missiles.dto.ReturnDTO;
import com.casic.missiles.dto.ReturnUtil;
import com.casic.missiles.dto.system.*;
import com.casic.missiles.enums.BusinessExceptionEnum;
import com.casic.missiles.enums.ExportEnum;
import com.casic.missiles.exception.BusinessException;
import com.casic.missiles.model.system.SystemFlowForm;
import com.casic.missiles.model.exception.enums.CoreExceptionEnum;
import com.casic.missiles.service.system.IFlowDefService;
import com.casic.missiles.service.system.ISystemFlowFormService;
import com.casic.missiles.utils.ConvertUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

/**
 * @Description: 流程管理
 *      功能：
 *      1.json转bpmn，里面传入了条件、审批人等信息，实现flowable流程定义生成、表单信息存储、表单及流程定义关联
 *      2.流程定义列表、编辑、详情、废止、删除、导出
 * @Author: wangpeng
 * @Date: 2022/12/12 16:46
 */
@Api(tags = "流程管理接口")
@Controller
@RequestMapping("/system/flow")
public class SystemFlowDefController extends ExportController {
    @Autowired
    private IFlowDefService processService;
    @Autowired
    private ISystemFlowFormService flowFormService;

    /**
     * 保存流程定义，流程实例中才要传业务表id
     * 该接口同时实现存储表单信息、流程定义信息
     * 前端传入workFlowDef实体中增加flowKey字段，用于更新流程定义
     * 前端传入workFlowDef实体中增加directorId字段（用户表用户id），用于作为流程负责人
     * 前端传入_value实体中增加formId、formName、formDesc，flowDef用nodeConfig
     */
    @ApiOperation("加载流程定义（jsonToBpmn）")
    @PostMapping("/jsonToBpmn")
    @ResponseBody
    public Object jsonToBpmn(@RequestBody JSONObject jsonObject) throws InvocationTargetException, IllegalAccessException {
        System.err.println(jsonObject.toJSONString());
        ReturnDTO result = processService.jsonToBpmn(jsonObject);
        return result;
    }

    /**
     * 流程定义列表
     * 关联查询ACT_RE_PROCDEF（流程定义表）、ACT_RE_DEPLOYMENT（流程部署表，一个流程定义id一个部署id，一个流程定义有多个版本）
     */
    @ApiOperation("流程定义列表（分页）")
    @PostMapping("/listPage")
    @ResponseBody
    public ReturnDTO<PageInfoBT<FlowProcDefDTO>> listPage(@RequestBody FlowListRequest request) {
        Page<FlowProcDefDTO> page = PageFactory.defaultPage();
        List<FlowProcDefDTO> flowProcDefDTOList = processService.listPage(page, request);
        page.setRecords(flowProcDefDTOList);
        return ReturnUtil.success(super.packForBT(page));
    }

    /**
     * 流程定义编辑
     * 钉钉vue2中编辑会根据生成的prefix+templateId生成流程id，流程id相同flowable会增加一个新版本的流程定义
     */
    @ApiOperation("流程定义编辑")
    @PostMapping("/update")
    @ResponseBody
    public ReturnDTO update(@RequestBody JSONObject jsonObject) throws InvocationTargetException, IllegalAccessException {
        return processService.update(jsonObject);
    }

    /**
     * 流程定义详情
     * 参考钉钉vue2中process_templates表会保存最新的模板信息（包括表单信息、流程定义信息）
     * 看是否能根据部署id获取flowable中流程定义json，不能则将流程定义json存到流程表单表中
     *      全局流程变量是在流程实例启动时设置的，在钉钉vue2通过流程实例id查看详情接口中可以获取流程实例及实时流程进程图
     *      所以将流程定义json存到表单表中，表单与流程定义多对一
     */
    @ApiOperation("流程定义详情")
    @PostMapping("/detail")
    @ResponseBody
    public ReturnDTO<JSONObject> detail(@RequestBody @Valid FlowDetailRequest request, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            throw new BusinessException(CoreExceptionEnum.REQUEST_NULL.getCode(), bindingResult.getFieldError().getDefaultMessage());
        }
        return processService.detail(request.getFormId());
    }

    /**
     * 流程定义废止
     * 参考RuoYi激活或挂起流程定义
     */
    @ApiOperation("流程定义挂起/激活（废止/挂起）")
    @PostMapping("/updateState")
    @ResponseBody
    public ReturnDTO updateState(@RequestBody @Valid FlowUpdateStateRequest request, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            throw new BusinessException(CoreExceptionEnum.REQUEST_NULL.getCode(), bindingResult.getFieldError().getDefaultMessage());
        }
        return processService.updateState(request);
    }

    /**
     * 流程定义删除
     * 参考RuoYi删除流程定义
     */
    @ApiOperation("流程定义删除")
    @PostMapping("/delete")
    @ResponseBody
    public ReturnDTO delete(@RequestBody @Valid FlowDeleteRequest request, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            throw new BusinessException(CoreExceptionEnum.REQUEST_NULL.getCode(), bindingResult.getFieldError().getDefaultMessage());
        }
        return processService.delete(request);
    }

    @ApiOperation("流程定义导出")
    @PostMapping("/listExport")
    @ResponseBody
    public void export(@RequestBody FlowListRequest request) throws IOException {
        List<FlowProcDefDTO> list;
        if (!CollectionUtils.isEmpty(request.getIds())) {
            list = processService.listByIds(request.getIds());
        } else {
            list = processService.list(request);
        }
        super.exportExcel(FlowProcDefDTO.class, list, ExportEnum.FLOW_DEF_EXPORT.getSheetName());
    }

    /**
     * 流程表单信息列表
     */
    @ApiOperation("流程表单信息列表")
    @GetMapping("/flowFormList")
    @ResponseBody
    public ReturnDTO<List<FlowFormListResponse>> flowFormList() {
        List<SystemFlowForm> list = flowFormService.list();
        List<FlowFormListResponse> flowFormListResponses = ConvertUtils.sourceToTarget(list, FlowFormListResponse.class);
        return ReturnUtil.success(flowFormListResponses);
    }

    /**
     * 获取发起人主管层级数
     *      主管的角色是组织负责人（TIPS字段为director）
     *      需要保证用户所在组织机构一定要有个角色的TIPS为director的角色的人
     */
    @ApiOperation("获取发起人主管层级数")
    @GetMapping("/directorLevel")
    @ResponseBody
    public ReturnDTO directorLevel(Long userId) {
        Assert.isFalse(Objects.isNull(userId), () -> {
            throw new BusinessException(BusinessExceptionEnum.ID_NULL);
        });
        int level = flowFormService.getDirectorLevel(userId);
        if(0 == level){
            return ReturnUtil.failed("该用户无部门主管或该用户为系统管理员！");
        }
        HashMap<String, Integer> map = new HashMap<>();
        map.put("level", level);
        return ReturnUtil.success(map);
    }
}
