package com.trade.admin.workflow.util;


import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.trade.common.constant.HttpStatus;
import com.trade.common.core.domain.BaseEntity;
import com.trade.common.exception.ServiceException;
import com.trade.common.utils.ConstantUtil;
import com.trade.common.utils.DateUtils;
import com.trade.common.utils.SpringUtils;

import java.lang.reflect.Method;
import java.util.Date;

/**
 * @Description:工作流引擎生成businessKey和解析其业务数据工具类
 * @Author:wanxiangZhou
 * @E-mail:zhouyu@sunxung.cn
 * @Date:2018年6月25日 下午4:17:47
 */
public class FlowableUtil {

    /**
     * 分隔符
     **/
    public static final String splitChar = "_";

    /**
     * 生成Flowable工作流引擎businessKey值<br>
     * 约定说明：针对一条源(主)业务对应多个业务流程，如合同付款信息可对应多个放款申请流程;<br>
     * 这种情况，返回值格式为"源业务类别clazz_源业务ID_流程业务ID_搜索条件"第三个必须为流程业务ID因为FlowableUtil.getBusinessDateById()方法需要据此获取业务对象。
     *
     * @param entity        业务实体
     * @param key           流程管理key
     * @param queryBusiness 业务查询条件数据
     * @return
     */
    public static String generateBusinessKey(BaseEntity entity, String key, String queryBusiness) {
        if (null != entity) {
            if (StrUtil.isNotEmpty(queryBusiness)) {
                return key + splitChar + entity.getId() + splitChar + queryBusiness;
            }
            return key + splitChar + entity.getId();
        }
        return null;
    }

    /**
     * 根据Flowable工作流引擎业务KEY，获取具体业务对象
     *
     * @param businessKey 工作流业务KEY
     * @return 业务实体对象
     */
    public static Object getBusinessDateById(String businessKey) {
        if (StrUtil.isNotEmpty(businessKey)) {
            String[] result = businessKey.split(splitChar);
            Object service;
            if (result.length >= 2) {
                String className = result[0];
                String id = result[1];
                service = SpringUtils.getBean(className.substring(0, 1).toLowerCase() + className.substring(1) + "Service");
                try {
                    Method method = service.getClass().getMethod("get", Long.class);
                    return method.invoke(service, Long.parseLong(id));
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new ServiceException("获取流程业务数据出错啦！" + e.getMessage(), HttpStatus.ERROR);
                }
            }
        }
        return null;
    }

    /**
     * 获取表单业务信息
     *
     * @param serviceName    业务service名称
     * @param formBusinessId 表单业务ID
     * @return 表单业务对象
     */
    public static Object getFormBusinessDateById(String serviceName, Long formBusinessId) {
        if (null != formBusinessId && StrUtil.isNotEmpty(serviceName)) {
            Object service = SpringUtils.getBean(serviceName + "Service");
            try {
                Method method = service.getClass().getMethod("getView", Long.class);
                return method.invoke(service, formBusinessId);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("获取表单业务数据出错啦！" + e.getMessage(), HttpStatus.ERROR);
            }
        }
        return null;
    }

    /**
     * 工作日时限：获取业务办理倒计时详情（最小单位：0.5天）
     *
     * @param createTime 任务开始时间
     * @param timeLimit  任务时限
     * @return 负数代表逾期天数；正数代表倒计时，未逾期
     */
    public static Float getBusinessDeadlineByWorkingDay(Date createTime, Float timeLimit) {
        if (null != createTime && null != timeLimit && timeLimit > 0) {
            Date now = new Date();
            Date endTime = null;// 逾期截止日期
            float deadline = 0f;
            // 判断任务开始时间是上午还是下午，设置临界时间点
            if (DateUtil.isWeekend(createTime)) {
                endTime = DateUtils.atEndOfDay(createTime);
            } else {
                if (ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(createTime))) {
                    endTime = DateUtils.setTimeToPM(createTime);// 从当前日期11:59:59开始计时
                } else {
                    endTime = DateUtils.atEndOfDay(createTime);// 从当前日期23:59:59开始计时
                }
            }
            Integer day = null;// 整天
            Integer half = null;// 半天
            String[] strs = timeLimit.toString().split("\\.");
            if (ArrayUtil.isNotEmpty(strs)) {
                day = Integer.parseInt(strs[0]);
                half = Integer.parseInt(strs[1]);
                if (day > 0) {
                    for (int i = 0; i < day; i++) {
                        endTime = DateUtils.plusDay(endTime, 1);
                        if (DateUtil.isWeekend(endTime)) {// 要求：同为工作日或自然日为单位进行倒计时计算
                            --i;
                        }
                    }
                }
                if (half > 0) {
                    endTime = DateUtils.plusHour(endTime, 12);// 半天以12小时为标准
                    int none = 100;// 防止死循环一
                    while (none > 0) {
                        if (DateUtil.isWeekend(endTime)) {
                            endTime = DateUtils.plusDay(endTime, 1);
                            none--;
                        } else {
                            break;
                        }
                    }
                }
                if (now.after(endTime)) {// 当前日期位于逾期截止日期之后，说明逾期中
                    // 直接过滤掉当天的情况，把endTime日期直接设置成第二天开始，便于在工作日信息比对
                    if (ConstantUtil.PM_CNAME.equals(DateUtils.getCnameOfAMPM(endTime))) {// 下午的临界点为23:59:59，加1h变成第二天
                        endTime = DateUtils.plusHour(endTime, 1);
                    } else {// 上午临界点11:59:59，加13h变成第二天
                        endTime = DateUtils.plusHour(endTime, 13);
                        deadline = 0.5f;
                    }
                    Date endOfEndTime = DateUtils.atStartOfDay(endTime);
                    for (Date wdm = DateUtils.atStartOfDay(now); !wdm.after(endOfEndTime); wdm = DateUtils.plusDay(wdm, 1)) {
                        if (!DateUtil.isWeekend(wdm)) {
                            if (DateUtils.onTheSameDay(wdm, now)
                                    && ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(now))) {// 这是最后一天，判断上午下午，其结果是0.5天还是1天
                                deadline = deadline + 0.5f;
                            } else {
                                deadline = deadline + 1f;
                            }
                        }
                    }

                    if (deadline > 0) {
                        return -deadline;
                    }
                } else {// 未逾期，计算倒计时
                    // 如果当前时间跟任务开始日期为同一天，则倒计时即为节点时限
                    if (DateUtils.onTheSameDay(now, createTime)) {
                        return timeLimit;
                    }

                    boolean firstFlag = false;
                    Date endOfEndTime = DateUtils.atStartOfDay(endTime);
                    for (Date wdm = DateUtils.atStartOfDay(now); !wdm.after(endOfEndTime); wdm = DateUtils.plusDay(wdm, 1)) {
                        if (DateUtil.isWeekend(wdm)) {
                            if (DateUtils.onTheSameDay(now, wdm)) {// 第一天直接过滤掉，不符合
                                firstFlag = true;
                            }
                        } else {
                            if (!firstFlag) {
                                if (ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(now))) {// 开始第一天（同一天情况已return过滤了），与逾期逻辑相反
                                    deadline = deadline + 1f;
                                } else {
                                    deadline = deadline + 0.5f;
                                }
                                firstFlag = true;// 重置值
                            } else {
                                if (null != endTime && (DateUtils.onTheSameDay(endTime, wdm))) {// 最后一天
                                    if (ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(endTime))) {
                                        deadline = deadline + 0.5f;
                                    } else {
                                        deadline = deadline + 1f;
                                    }
                                } else {// 中间天数直接+1
                                    deadline = deadline + 1f;
                                }
                            }
                        }
                    }

                    if (deadline > 0) {
                        return deadline;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 自然日时限：获取业务办理倒计时详情（最小单位：0.5天）
     *
     * @param createTime 任务开始时间
     * @param timeLimit  任务时限
     * @return 负数代表逾期天数；正数代表倒计时，未逾期
     */
    public static Float getBusinessDeadlineByNatureDay(Date createTime, Float timeLimit) {
        if (null != createTime && null != timeLimit && timeLimit > 0) {
            Date now = new Date();
            Date endTime = null;// 逾期截止日期
            String overdue = null;// 倒计时
            String createTimeAMPM = null;// 标识createTime是上午还是下午
            // 判断任务开始时间是上午还是下午，设置临界时间点
            if (ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(createTime))) {
                createTimeAMPM = ConstantUtil.AM_CNAME;
                endTime = DateUtils.setTimeToPM(createTime);// 从当前日期11:59:59开始计时
            } else {
                createTimeAMPM = ConstantUtil.PM_CNAME;
                endTime = DateUtils.atEndOfDay(createTime);// 从当前日期23:59:59开始计时
            }
            Integer day = null;// 整天
            Integer half = null;// 半天
            String[] liStrs = timeLimit.toString().split("\\.");
            if (liStrs.length > 1) {
                day = Integer.parseInt(liStrs[0]);
                half = Integer.parseInt(liStrs[1]);
                if (day > 0) {
                    endTime = DateUtils.plusDay(endTime, day);
                }
                if (half > 0) {
                    endTime = DateUtils.plusHour(endTime, 12);// 半天以12小时为标准
                }
                if (now.after(endTime)) {// 当前日期位于逾期截止日期之后，说明逾期了
                    overdue = DateUtils.betweenDays(endTime, now);
                    if (StrUtil.isNotEmpty(overdue)) {
                        return -Float.parseFloat(overdue);
                    }
                } else {// 未逾期，计算倒计时
                    // 如果当前时间跟任务开始日期为同一天，且判断上午还是下午，则同理设置当前日期对应的上午或下午的临界时间点
                    if (DateUtils.onTheSameDay(now, createTime)) {
                        if (ConstantUtil.AM_CNAME.equals(createTimeAMPM)
                                && ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(now))) {
                            now = DateUtils.setTimeToPM(now);
                        } else if (ConstantUtil.PM_CNAME.equals(createTimeAMPM)
                                && ConstantUtil.PM_CNAME.equals(DateUtils.getCnameOfAMPM(now))) {
                            now = DateUtils.atEndOfDay(now);
                        }
                    }
                    overdue = DateUtils.betweenDays(now, endTime);
                    if (StrUtil.isNotEmpty(overdue)) {
                        return Float.parseFloat(overdue);
                    }
                }
            }
        }
        return null;
    }

    /**
     * 自然日时限（逾期）：获取业务办理逾期详情（最小单位：0.5天）
     *
     * @param createTime 任务开始时间
     * @param timeLimit  任务时限
     * @return 逾期
     */
    public static Float getBusinessOverdueByNatureDay(Date createTime, Float timeLimit) {
        if (null != createTime && null != timeLimit && timeLimit > 0) {
            Date now = new Date();
            Date endTime = null;// 逾期截止日期
            String overdue = null;// 倒计时
            // 判断任务开始时间是上午还是下午，设置临界时间点
            if (ConstantUtil.AM_CNAME.equals(DateUtils.getCnameOfAMPM(createTime))) {
                endTime = DateUtils.setTimeToPM(createTime);// 从当前日期11:59:59开始计时
            } else {
                endTime = DateUtils.atEndOfDay(createTime);// 从当前日期23:59:59开始计时
            }
            Integer day = null;// 整天
            Integer half = null;// 半天
            String[] liStrs = timeLimit.toString().split("\\.");
            if (liStrs.length > 1) {
                day = Integer.parseInt(liStrs[0]);
                half = Integer.parseInt(liStrs[1]);
                if (day > 0) {
                    endTime = DateUtils.plusDay(endTime, day);
                }
                if (half > 0) {
                    endTime = DateUtils.plusHour(endTime, 12);// 半天以12小时为标准
                }
                if (now.after(endTime)) {// 当前日期位于逾期截止日期之后，说明逾期了
                    overdue = DateUtils.betweenDays(endTime, now);
                    if (StrUtil.isNotEmpty(overdue)) {
                        return -Float.parseFloat(overdue);
                    }
                }
            }
        }
        return null;
    }

}
