package com.fjhx.feign;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fjhx.base.BaseEntity;
import com.fjhx.constants.FlowConstant;
import com.fjhx.constants.FlowExplainConstant;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.example.ExampleDetails;
import com.fjhx.entity.example.ExampleInfo;
import com.fjhx.entity.process.ProcessInfo;
import com.fjhx.entity.process.ProcessNode;
import com.fjhx.entity.process.ProcessNodeButton;
import com.fjhx.entity.process.ProcessTenant;
import com.fjhx.enums.ButtonNameEnum;
import com.fjhx.enums.HandleResultEnum;
import com.fjhx.enums.ProcessNodeHandleObjectTypeEnum;
import com.fjhx.params.ExampleResult;
import com.fjhx.params.JumpVo;
import com.fjhx.service.example.ExampleDetailsService;
import com.fjhx.service.example.ExampleInfoService;
import com.fjhx.service.process.ProcessInfoService;
import com.fjhx.service.process.ProcessNodeButtonService;
import com.fjhx.service.process.ProcessNodeService;
import com.fjhx.service.process.ProcessTenantService;
import com.fjhx.utils.Assert;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
public class FlowClient implements IFlowClient {

    @Autowired
    private ExampleInfoService exampleInfoService;

    @Autowired
    private ProcessInfoService processInfoService;

    @Autowired
    private ProcessTenantService processTenantService;

    @Autowired
    private ProcessNodeService processNodeService;

    @Autowired
    private ProcessNodeButtonService processNodeButtonService;

    @Autowired
    private ExampleDetailsService exampleDetailsService;

    @Transactional(rollbackFor = Exception.class)
    @GetMapping(CREATE)
    @Override
    public R<ExampleResult> create(Long flowLinkNo, String code, String nodeCode, String title, String remarks, String cacheData) {

        ExampleResult exampleResult = new ExampleResult();

        Long count = exampleInfoService.count(Wrappers.<ExampleInfo>lambdaQuery()
                .eq(ExampleInfo::getFlowLinkNo, flowLinkNo)
                .eq(ExampleInfo::getComplete, StatusConstant.NO));

        Assert.eqZero(count, FlowExplainConstant.EXPLAIN_NOT_EXIST);

        // 查找通用流程
        ProcessInfo processInfo = processInfoService.getOne(ProcessInfo::getCode, code);
        Assert.notEmpty(processInfo, String.format(FlowExplainConstant.PROCESS_NOT_EXIST, code));

        // 获取租户流程
        ProcessTenant processTenant = processTenantService.getOne(Wrappers.<ProcessTenant>lambdaQuery()
                .eq(ProcessTenant::getProcessInfoId, processInfo.getId())
                .eq(ProcessTenant::getBindingTenantId, AuthUtil.getTenantId())
                .eq(ProcessTenant::getCurrentVersion, StatusConstant.YES));

        // 创建流程实例
        ExampleInfo exampleInfo = new ExampleInfo();
        exampleInfo.setProcessInfoId(processInfo.getId());
        exampleInfo.setTitle(title);
        exampleInfo.setFlowLinkNo(flowLinkNo);
        exampleInfo.setCacheData(cacheData);

        // 走默认流程，直接完成
        if (processTenant == null) {
            exampleInfo.setComplete(StatusConstant.YES);
            exampleInfo.setProcessNodeId(FlowConstant.OVER_PROCESS_FLAG);
            exampleInfo.setProcessNodeCode(FlowConstant.END_CODE);
            exampleInfo.setFlowType(FlowConstant.FLOW_TYPE_DEFAULT);
            exampleInfo.setHandleResult(HandleResultEnum.SUCCESS.getType());
            exampleInfoService.save(exampleInfo);

            // 开始流程明细
            ExampleDetails startDetails = new ExampleDetails();
            startDetails.setExampleInfoId(exampleInfo.getId());
            startDetails.setProcessInfoId(processInfo.getId());
            startDetails.setNameType(ButtonNameEnum.START.getType());
            startDetails.setHandleUserId(AuthUtil.getUserId());
            startDetails.setRemarks(remarks);

            // 结束流程明细
            ExampleDetails endDetails = new ExampleDetails();
            endDetails.setExampleInfoId(exampleInfo.getId());
            endDetails.setProcessInfoId(processInfo.getId());
            endDetails.setNameType(ButtonNameEnum.END.getType());

            // 封装返回值
            exampleResult.setOldExampleInfo(exampleInfo);
            exampleResult.setExampleInfo(exampleInfo);
            exampleResult.setExampleDetailsIdList(Arrays.asList(startDetails.getId(), endDetails.getId()));
            exampleResult.setHandleType(ButtonNameEnum.END.getType());
            exampleResult.setCacheDataStr(cacheData);
            return R.data(exampleResult);
        }

        // 查询节点
        ProcessNodeButton processNodeButton = processNodeButtonService.getOne(Wrappers.<ProcessNodeButton>lambdaQuery()
                .eq(ProcessNodeButton::getProcessTenantId, processTenant.getId())
                .eq(ProcessNodeButton::getProcessNodeCode, FlowConstant.START_CODE));
        Assert.notEmpty(processNodeButton, FlowExplainConstant.SPECIFY_BRANCH_EMPTY);

        exampleInfo.setFlowType(FlowConstant.FLOW_TYPE_TENANT);
        exampleInfo.setProcessTenantId(processTenant.getId());
        exampleInfo.setProcessNodeId(processNodeButton.getProcessNodeId());
        exampleInfo.setProcessNodeCode(processNodeButton.getProcessNodeCode());
        exampleInfo.setComplete(StatusConstant.NO);
        exampleInfo.setHandleResult(HandleResultEnum.IN_PROGRESS.getType());
        exampleInfoService.save(exampleInfo);

        JumpVo jumpVo = new JumpVo();
        setJumpNode(jumpVo, processNodeButton, nodeCode);
        exampleResult.setJumpVo(jumpVo);

        return R.data(exampleResult);
    }

    @GetMapping(GET_JUMP_VO)
    @Override
    public R<JumpVo> getJumpVo(Long buttonId, String nodeCode) {
        JumpVo jumpVo = new JumpVo();

        ProcessNodeButton processNodeButton = processNodeButtonService.getById(buttonId);
        Assert.notEmpty(processNodeButton, "nodeButton is null");

        setJumpNode(jumpVo, processNodeButton, nodeCode);

        return R.data(jumpVo);
    }


    @Transactional(rollbackFor = Exception.class)
    @PostMapping(JUMP)
    @Override
    public R<ExampleResult> jump(JumpVo jumpVo) {

        // 返回值
        ExampleResult exampleResult = new ExampleResult();

        // 查询流程实例
        ExampleInfo oleExampleInfo = exampleInfoService.getOne(Wrappers.<ExampleInfo>lambdaQuery()
                .eq(ExampleInfo::getFlowLinkNo, jumpVo.getFlowLinkNo())
                .eq(ExampleInfo::getComplete, StatusConstant.NO));
        Assert.notEmpty(oleExampleInfo, FlowExplainConstant.EXAMPLE_INFO_NULL);

        // 复制除一个对象做修改
        ExampleInfo exampleInfo = ObjectUtil.clone(oleExampleInfo);

        String cacheDataStr = jumpVo.getCacheDataStr();
        if (ObjectUtil.isNotEmpty(cacheDataStr)) {
            exampleInfo.setCacheData(cacheDataStr);
        }

        // 明细
        ExampleDetails exampleDetails = createExampleDetails(exampleInfo, jumpVo.getNameType());
        exampleDetails.setProcessNodeButtonId(jumpVo.getButtonId());
        exampleDetails.setHandleUserId(AuthUtil.getUserId());
        exampleDetails.setRemarks(jumpVo.getRemarks());

        // 赋值按钮处理方法
        exampleResult.setHandleType(jumpVo.getNameType());

        // 驳回
        if (ButtonNameEnum.REJECT.getType().equals(jumpVo.getNameType())) {
            exampleInfo.setHandleResult(HandleResultEnum.FAIL.getType());
        }
        // 通过
        else if (FlowConstant.END_CODE.equals(jumpVo.getJumpNodeCode())) {
            exampleInfo.setHandleResult(HandleResultEnum.SUCCESS.getType());
            // 赋值结束处理方法
            exampleResult.setHandleType(ButtonNameEnum.END.getType());
        }

        exampleInfo.setProcessNodeId(jumpVo.getJumpNodeId());
        exampleInfo.setProcessNodeCode(jumpVo.getJumpNodeCode());
        exampleInfo.setHandleObjectType(jumpVo.getHandleObjectType());
        exampleInfo.setHandleObjectIdSet(jumpVo.getHandleObjectIdSet());

        List<Long> exampleDetailsIdList = new ArrayList<>();

        // 流程结束
        if (FlowConstant.END_CODE.equals(jumpVo.getJumpNodeCode())) {
            // 赋值流程完成
            exampleInfo.setComplete(StatusConstant.YES);

            // 流程明细
            ExampleDetails endExampleDetails = createExampleDetails(exampleInfo, ButtonNameEnum.END.getType());
            exampleDetailsService.saveBatch(Arrays.asList(exampleDetails, endExampleDetails));
            exampleDetailsIdList.add(exampleDetails.getId());
            exampleDetailsIdList.add(endExampleDetails.getId());
        }
        // 正常跳转
        else {
            // 流程明细
            exampleDetailsService.save(exampleDetails);
            exampleDetailsIdList.add(exampleDetails.getId());

            // 如果跳转节点到发起（一直回退到发起人），处理人只能是发起人
            if (FlowConstant.START_CODE.equals(jumpVo.getJumpNodeCode())) {
                exampleInfo.setHandleObjectType(ProcessNodeHandleObjectTypeEnum.USER.getType());
                exampleInfo.setHandleObjectIdSet(exampleInfo.getCreateUser().toString());
            }
        }

        exampleInfoService.updateById(exampleInfo);

        exampleResult.setOldExampleInfo(oleExampleInfo);
        exampleResult.setExampleInfo(exampleInfo);
        exampleResult.setCacheDataStr(exampleInfo.getCacheData());
        exampleResult.setExampleDetailsIdList(exampleDetailsIdList);

        return R.data(exampleResult);
    }

    @PostMapping(ROLL_BACK)
    @Override
    public void rollBack(ExampleResult result) {
        ExampleInfo oldExampleInfo = result.getOldExampleInfo();

        if (result.isDeleteExample()) {
            exampleInfoService.update(Wrappers.<ExampleInfo>lambdaUpdate()
                    .eq(BaseEntity::getId, oldExampleInfo.getId())
                    .set(ExampleInfo::getDelFlag, 1)
                    .set(ExampleInfo::getRemarks, "业务异常,本次流程实例已删除")
            );
        } else {
            exampleInfoService.updateById(oldExampleInfo);
        }

        exampleDetailsService.update(Wrappers.<ExampleDetails>lambdaUpdate()
                .in(BaseEntity::getId, result.getExampleDetailsIdList())
                .set(ExampleDetails::getDelFlag, 1)
                .set(ExampleDetails::getRemarks, "业务异常,本次流程明细已删除")
        );
    }

    @Override
    public R withdraw(Long withdrawExampleInfoId, Long withdrawExampleDetailsId) {
        exampleInfoService.withdraw(withdrawExampleInfoId, withdrawExampleDetailsId);
        return R.success();
    }

    @Override
    public R revoke(Long revokeExampleInfoId) {
        exampleInfoService.revoke(revokeExampleInfoId);
        return R.success();
    }


    private void setJumpNode(JumpVo jumpVo, ProcessNodeButton processNodeButton, String nodeCode) {
        // 赋值按钮名称类型
        jumpVo.setNameType(processNodeButton.getNameType());
        jumpVo.setButtonId(processNodeButton.getId());

        ProcessNode processNode;

        // 非分支跳转
        if (ObjectUtil.isNotEmpty(processNodeButton.getJumpNodeId())) {
            processNode = processNodeService.getById(processNodeButton.getJumpNodeId());
        }
        // 分支跳转
        else {
            // 跳转节点编码不为空
            Assert.notEmpty(nodeCode, FlowExplainConstant.SPECIFY_BRANCH_EMPTY);

            // 查询跳转到哪个分支节点
            processNode = processNodeService.getOne(Wrappers.<ProcessNode>lambdaQuery()
                    .eq(ProcessNode::getParentId, processNodeButton.getProcessNodeId())
                    .eq(ProcessNode::getCode, nodeCode));

            // 分支节点不为空
            Assert.notEmpty(processNode, FlowExplainConstant.SPECIFY_BRANCH_EMPTY);
        }

        jumpVo.setJumpNodeId(processNode.getId());
        jumpVo.setJumpNodeCode(processNode.getCode());
        jumpVo.setHandleObjectType(processNode.getHandleObjectType());
        jumpVo.setHandleObjectIdSet(processNode.getHandleObjectIdSet());

    }

    /**
     * 创建流程实例明细
     */
    private ExampleDetails createExampleDetails(ExampleInfo exampleInfo, Integer nameType) {
        ExampleDetails exampleDetails = new ExampleDetails();
        exampleDetails.setExampleInfoId(exampleInfo.getId());
        exampleDetails.setProcessInfoId(exampleInfo.getProcessInfoId());
        exampleDetails.setProcessTenantId(exampleInfo.getProcessTenantId());
        exampleDetails.setProcessNodeId(exampleInfo.getProcessNodeId());
        exampleDetails.setNameType(nameType);
        return exampleDetails;
    }

    /**
     * 根据流程实例ID删除流程（伪删除）
     *
     * @param exampleInfoId 流程实例ID
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Boolean deleteByExampleInfoId(Long exampleInfoId) {
        Boolean flag = true;
        try {
            //流程实例
            UpdateWrapper<ExampleInfo> exampleInfoWrapper = new UpdateWrapper<>();
            exampleInfoWrapper.lambda().set(ExampleInfo::getDelFlag, 1);
            exampleInfoWrapper.lambda().eq(ExampleInfo::getId, exampleInfoId);
            exampleInfoService.update(exampleInfoWrapper);

            //实例明细
            UpdateWrapper<ExampleDetails> exampleDetailsWrapper = new UpdateWrapper<>();
            exampleDetailsWrapper.lambda().set(ExampleDetails::getDelFlag, 1);
            exampleDetailsWrapper.lambda().eq(ExampleDetails::getExampleInfoId, exampleInfoId);
            exampleDetailsService.update(exampleDetailsWrapper);
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
            throw e;
        } finally {
            return flag;
        }
    }

    /**
     * 获取当前节点对应的按钮ID
     *
     * @param exampleInfoId 流程实例ID
     * @return
     */
    @Override
    public Long getButtonIdByExampleInfoId(Long exampleInfoId) {
        Long buttonId = null;
        //查询流程实例信息
        ExampleInfo exampleInfo = exampleInfoService.getById(exampleInfoId);
        if (Func.isNotEmpty(exampleInfo)) {
            //查询节点按钮信息
            QueryWrapper<ProcessNodeButton> wrapper = new QueryWrapper<>();
            wrapper.lambda().eq(ProcessNodeButton::getProcessNodeId, exampleInfo.getProcessNodeId());
            ProcessNodeButton button = processNodeButtonService.getOne(wrapper);
            if (Func.isNotEmpty(button)) {
                buttonId = button.getId();
            }
        }
        return buttonId;
    }
}
