package cn.stylefeng.workflow.instance.modular.task.strategy.agree.base;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.ApproverNodeLeaveTypeEnum;
import cn.stylefeng.workflow.api.enums.ApproverNodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.config.node.approver.ApproverStrategy;
import cn.stylefeng.workflow.api.pojo.config.node.approver.detail.NodeApproverPerson;
import cn.stylefeng.workflow.instance.modular.task.pojo.NodeTaskCountVo;
import cn.stylefeng.workflow.instance.modular.task.service.RuTaskService;
import cn.stylefeng.workflow.instance.modular.task.strategy.agree.BaseAgreeStrategy;
import cn.stylefeng.workflow.instance.modular.task.strategy.agree.BaseLeaveRule;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
public class AgreeAndStrategy implements BaseAgreeStrategy {

    private final boolean purePredictFlag;

    public AgreeAndStrategy(boolean purePredictFlag) {
        this.purePredictFlag = purePredictFlag;
    }

    @Override
    public boolean getStrategyExecuteFlag(Integer approverNodeTypeEnum) {
        return ApproverNodeTypeEnum.AND_APPROVE.getMode().equals(approverNodeTypeEnum);
    }

    @Override
    public boolean strategyAction(RuTask currentProcessTask, ApproverStrategy approverStrategy) {

        RuTaskService ruTaskService = SpringUtil.getBean(RuTaskService.class);

        if (ApproverNodeLeaveTypeEnum.PERCENT.getCode().equals(approverStrategy.getApproverNodeLeaveType())) {

            NodeTaskCountVo nodeTaskCount = this.predictableNodeTaskCount(currentProcessTask.getRuNodeId(), ruTaskService);

            if (nodeTaskCount == null) {
                return true;
            }

            BigDecimal currentPercent = new BigDecimal(nodeTaskCount.getAlreadyAgreeCount()).divide(
                    new BigDecimal(nodeTaskCount.getNodeTaskCount()), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
            Integer approverNodeLeavePercent = approverStrategy.getApproverNodeLeavePercent();

            if (currentPercent.intValue() >= approverNodeLeavePercent) {
                ruTaskService.deleteAnyOtherTask(!purePredictFlag, currentProcessTask.getRuNodeId(), currentProcessTask.getTaskId());
                return true;
            } else {
                return false;
            }
        }

        else if (ApproverNodeLeaveTypeEnum.SUPER_NUMBER.getCode().equals(approverStrategy.getApproverNodeLeaveType())) {

            NodeTaskCountVo nodeTaskCount = this.predictableNodeTaskCount(currentProcessTask.getRuNodeId(), ruTaskService);

            if (nodeTaskCount == null) {
                return true;
            }

            if (nodeTaskCount.getAlreadyAgreeCount() > approverStrategy.getApproverNodeLeaveCount()) {
                ruTaskService.deleteAnyOtherTask(!purePredictFlag, currentProcessTask.getRuNodeId(), currentProcessTask.getTaskId());
                return true;
            } else {
                return false;
            }
        }

        else if (ApproverNodeLeaveTypeEnum.ONE_COMMIT.getCode().equals(approverStrategy.getApproverNodeLeaveType())) {

            NodeTaskCountVo nodeTaskCount = this.predictableNodeTaskCount(currentProcessTask.getRuNodeId(), ruTaskService);

            if (nodeTaskCount == null) {
                return true;
            }

            List<NodeApproverPerson> configPersonList = approverStrategy.getPersonAlreadyAddressedList();
            if (ObjectUtil.isEmpty(configPersonList)) {
                return true;
            }

            if (nodeTaskCount.getNodeTaskCount().equals(nodeTaskCount.getAlreadyAgreeCount())) {
                return true;
            }

            for (NodeApproverPerson configPerson : configPersonList) {
                if (currentProcessTask.getApprover().equals(configPerson.getUserId())) {
                    ruTaskService.deleteAnyOtherTask(!purePredictFlag, currentProcessTask.getRuNodeId(), currentProcessTask.getTaskId());
                    return true;
                }
            }

            return false;
        }

        else if (ApproverNodeLeaveTypeEnum.MANY_COMMIT.getCode().equals(approverStrategy.getApproverNodeLeaveType())) {

            List<NodeApproverPerson> configPersonList = approverStrategy.getPersonAllAddressedList();
            if (ObjectUtil.isEmpty(configPersonList)) {
                return true;
            }

            NodeTaskCountVo nodeTaskCount = this.predictableNodeTaskCount(currentProcessTask.getRuNodeId(), ruTaskService);

            if (nodeTaskCount == null) {
                return true;
            }

            Set<Long> agreeUserIdList = nodeTaskCount.getAlreadyAgreeUserIds();
            if (ObjectUtil.isEmpty(agreeUserIdList)) {
                return false;
            }

            if (nodeTaskCount.getNodeTaskCount().equals(nodeTaskCount.getAlreadyAgreeCount())) {
                return true;
            }

            for (NodeApproverPerson nodeApproverPerson : configPersonList) {
                boolean isContain = false;
                for (Long alreadyAgreeUserId : agreeUserIdList) {
                    if (alreadyAgreeUserId.equals(nodeApproverPerson.getUserId())) {
                        isContain = true;
                        break;
                    }
                }
                if (!isContain) {
                    return false;
                }
            }

            ruTaskService.deleteAnyOtherTask(!purePredictFlag, currentProcessTask.getRuNodeId(), currentProcessTask.getTaskId());
            return true;
        }

        else if (ApproverNodeLeaveTypeEnum.EXPAND_CLASS.getCode().equals(approverStrategy.getApproverNodeLeaveType())) {

            String customExpandRuleClass = approverStrategy.getCustomExpandRuleClass();

            Class<?> pointClass = null;
            try {
                pointClass = Class.forName(customExpandRuleClass);
            } catch (ClassNotFoundException e) {
                log.error("会签，针对自定义规则的类，找不到指定的类，类名为：{}", customExpandRuleClass);
            }

            if (pointClass != null) {
                try {
                    BaseLeaveRule baseAgreeStrategy = (BaseLeaveRule) pointClass.newInstance();
                    return baseAgreeStrategy.executeLeaveRule(currentProcessTask);
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error("会签，针对自定义规则的类，实例化失败，类名为：{}", customExpandRuleClass);
                }
            }

            return false;
        }

        return false;
    }

    public NodeTaskCountVo predictableNodeTaskCount(Long ruNodeId, RuTaskService ruTaskService) {

        NodeTaskCountVo nodeTaskCount = ruTaskService.getNodeTaskCount(ruNodeId);
        if (nodeTaskCount == null) {
            return null;
        }

        if (purePredictFlag) {

            nodeTaskCount.setAlreadyAgreeCount(nodeTaskCount.getAlreadyAgreeCount() + 1);

            Set<Long> alreadyAgreeUserIds = nodeTaskCount.getAlreadyAgreeUserIds();
            if (ObjectUtil.isEmpty(alreadyAgreeUserIds)) {
                alreadyAgreeUserIds = new HashSet<>();
            }
            alreadyAgreeUserIds.add(LoginContext.me().getLoginUser().getUserId());
            nodeTaskCount.setAlreadyAgreeUserIds(alreadyAgreeUserIds);
        }

        return nodeTaskCount;
    }

}
