package com.boco.nbd.wios.flow.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import lombok.extern.slf4j.Slf4j;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * @Description: TODO
 * @Author: ZQY
 * @Date: 2022/8/23
 **/
@Slf4j
public class CalculateHours {
    SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
    String beginWork = "08:30:00";
    String endWork = "18:00:00";
    //设置上班时间:该处时间可以根据实际情况进行调整
    /**
     * 星期六
     */
    int saturday=6;
    /**
     * 星期天
     */
    int sunday=0;

    /**
     * 上午上班时间,小时
     */
    int abh = 8;
    /**
     * 上午上班时间,分钟
     */
    int abm = 30;
    /**
     * 上午下班时间,小时
     */
    int aeh = 12;
    /**
     * 上午下班时间,分钟
     */
    int aem = 0;
    /**
     * 下午上班时间,小时
     */
    int pbh = 12;
    /**
     * 下午上班时间,小时
     */
    int pbm = 0;
    /**
     * 下午下班时间,小时
     */
    int peh = 18;
    /**
     * 下午下班时间,分钟
     */
    int pem = 0;
    float h1 = abh+(float)abm/60;
    float h2 = aeh+(float)aem/60;
    float h3 = pbh+(float)pbm/60;
    float h4 = peh+(float)pem/60;
    /**
     * 每天上班小时数
     */
    float hoursPerDay = h2-h1+(h4-h3);
    /**
     * 每周工作天数
     */
    int daysPerWeek = 5;
    /**
     * 每天的毫秒数
     */
    long milsecPerDay = 1000*60*60*24;
    /**
     * 每星期小时数
     */
    float hoursPerWeek = hoursPerDay*daysPerWeek;
    public float calculateHours(String beginTime, String endTime){
        //对输入的字符串形式的时间进行转换
        //真实开始时间
        Date t1 = stringToDate(beginTime);
        //真实结束时间
        Date t2 = stringToDate(endTime);
        //对时间进行预处理
        t1 = processBeginTime(t1,0);
        t2 = processEndTime(t2,0);
        //若开始时间晚于结束时间,返回0
        if(t1.getTime()>t2.getTime()){
            return 0;
        }
        //开始时间到结束时间的完整星期数
        int weekCount = (int) ((t2.getTime()-t1.getTime())/(milsecPerDay*7));
        float totalHours = 0;
        totalHours += weekCount * hoursPerWeek;
        //调整结束时间,使开始时间和结束时间在一个星期的周期之内
        t2.setTime(t2.getTime()-weekCount*7*milsecPerDay);
        //记录开始时间和结束时间之间工作日天数
        int dayCounts = 0;
        //调整开始时间,使得开始时间和结束时间在同一天,或者相邻的工作日内。
        while(t1.getTime()<=t2.getTime()){
            Date temp = new Date(t1.getTime()+milsecPerDay);
            temp = processBeginTime(temp,0);
            temp.setHours(t1.getHours());
            temp.setMinutes(t1.getMinutes());
            if(temp.getTime()>t2.getTime()){
                break;
            }else{
                t1 = temp;
                dayCounts++;
            }
        }
        totalHours += dayCounts * hoursPerDay;
        float hh1 = t1.getHours() + (float)t1.getMinutes()/60 + (float)t1.getSeconds()/3600;
        float hh2 = t2.getHours() + (float)t2.getMinutes()/60 + (float)t2.getSeconds()/3600;
        //处理开始结束是同一天的情况
        if(t1.getDay()==t2.getDay()){
            float tt = 0;
            tt = hh2 - hh1;
            if(hh1>=h1&&hh1<=h2&&hh2>=h3){
                tt = tt - (h3-h2);
            }
            totalHours += tt;
        }else{
            //处理开始结束不是同一天的情况
            float tt1 = h4 - hh1;
            float tt2 = hh2 - h1;
            if(hh1<=h2){
                tt1 = tt1 - (h3-h2);
            }
            if(hh2>=h3){
                tt2 = tt2 - (h3-h2);
            }
            totalHours += (tt1 + tt2);
        }
        return totalHours;
    }

    /**
     * 格式化输出时间: yyyy-mm-dd hh:mm:ss 星期x
     * @param t
     * @return
     */
    private static String printDate(Date t) {
        String str;
        String xingqi = null;
        switch (t.getDay()) {
            case 0:
                xingqi = "星期天";
                break;
            case 1:
                xingqi = "星期一";
                break;
            case 2:
                xingqi = "星期二";
                break;
            case 3:
                xingqi = "星期三";
                break;
            case 4:
                xingqi = "星期四";
                break;
            case 5:
                xingqi = "星期五";
                break;
            case 6:
                xingqi = "星期六";
                break;
            default:
                break;
        }
        str = xingqi;
        return str;
    }

    /**
     * 对结束时间进行预处理,使其处于工作日内的工作时间段内
     * @param t
     * @return
     */
    private Date processEndTime(Date t,int type) {
        float h = t.getHours() + (float)t.getMinutes()/60;
        //若结束时间晚于下午下班时间,将其设置为下午下班时间
        if(h>=h4){
            t.setHours(peh);
            t.setMinutes(pem);
        }else {
            //若结束时间介于中午休息时间,那么设置为上午下班时间
            if(h>=h2&&h<=h3){
                t.setHours(aeh);
                t.setMinutes(aem);
            }else{
                //若结束时间早于上午上班时间,日期向前推一天,并将时间设置为下午下班时间
                if(h<=h1){
                    t.setTime(t.getTime()-milsecPerDay);
                    t.setHours(peh);
                    t.setMinutes(pem);
                }
            }
        }
        //若结束时间是周末,那么将结束时间向前推移到最近的工作日的下午下班时间
        if(type==0){
            if(t.getDay()==sunday){
                t.setTime(t.getTime()-milsecPerDay*(t.getDay()==saturday?1:2));
                t.setHours(peh);
                t.setMinutes(pem);
            }
            if(t.getDay()==saturday){
                t.setTime(t.getTime()-milsecPerDay*(t.getDay()==saturday?1:2));
                t.setHours(peh);
                t.setMinutes(pem);
            }
        }

        return t;
    }
    /**
     * 对开始时间进行预处理
     * @param t
     * @return
     */
    private Date processBeginTime(Date t,int type) {
        float h = t.getHours() + (float)t.getMinutes()/60;
        //若开始时间晚于下午下班时间,将开始时间向后推一天
        if(h>=h4){
            t.setTime(t.getTime()+milsecPerDay);
            t.setHours(abh);
            t.setMinutes(abm);
        }else {
            //若开始时间介于中午休息时间,那么设置为下午上班时间
            if(h>=h2&&h<=h3){
                t.setHours(pbh);
                t.setMinutes(pbm);
            }else{
                //若开始时间早于上午上班时间,将hour设置为上午上班时间
                if(t.getHours()<=h1){
                    t.setHours(abh);
                    t.setMinutes(abm);
                }
            }
        }
        //若开始时间是周末,那么将开始时间向后推移到最近的工作日的上午上班时间
        if(type==0){
            if(t.getDay()==sunday){
                t.setTime(t.getTime()+milsecPerDay*(t.getDay()==saturday?2:1));
                t.setHours(abh);
                t.setMinutes(abm);
            }if(t.getDay()==saturday){
                t.setTime(t.getTime()+milsecPerDay*(t.getDay()==saturday?2:1));
                t.setHours(abh);
                t.setMinutes(abm);
            }
        }

        return t;
    }

    /**
     * 将字符串形式的时间转换成Date形式的时间
     * @param time
     * @return
     */
    private Date stringToDate(String time){
        try {
            return format.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 去除周末节假日工作小时
     * @param beginTime
     * @param endTime
     * @return
     * @throws ParseException
     */
    public static float calculateHour(String beginTime,String endTime,String[] workdayArr,String[] naturalArr) {

        try {
            CalculateHours ch = new CalculateHours();
            float a = ch.calculateHours(beginTime, endTime);
            Calendar startDay = Calendar.getInstance();
            Calendar endDay = Calendar.getInstance();
            startDay.setTime(FORMATTER.parse(beginTime));
            endDay.setTime(FORMATTER.parse(endTime));

            Calendar naturalStartDay = Calendar.getInstance();
            Calendar naturalEndDay = Calendar.getInstance();
            naturalStartDay.setTime(FORMATTER.parse(beginTime));
            naturalEndDay.setTime(FORMATTER.parse(endTime));

            String[] workday = printDay(startDay, endDay);
            String[] naturalDay = printNaturalDay(naturalStartDay, naturalEndDay);
//        String[] holiday = new String[]{"01-01","01-02","01-03","05-01","05-02","05-03","10-01","10-02",
//                "10-03","10-04","10-05","10-06","02-08","02-09","02-10","09-12","04-04","04-05","04-06"};
//        String[] restDay = new String[]{"04-02","04-03"};
//        Calendar now = Calendar.getInstance();
//        int year=now.get(Calendar.YEAR); //获取当前年份
//
//        //工作日集合
//        List<String> workdayList = new ArrayList<String>();
//        for (String string : holiday) {
//            string=year+"-"+string;
//            workdayList.add(string);
//        }
//        String[] workdayArr = workdayList.toArray(new String[0]);
//
//        //自然日集合
//        List<String> naturalList = new ArrayList<String>();
//        for (String string : restDay) {
//            string=year+"-"+string;
//            naturalList.add(string);
//        }
//        String[] naturalArr = naturalList.toArray(new String[0]);


            List<String> holidayDayList = arrContrastRest(workday, workdayArr);

            List<String> restDayList = arrContrastRest(naturalDay, naturalArr);


            Double holidayHous = getRestDayHous(holidayDayList, beginTime, endTime);
            Double workDayHous = getRestDayHous(restDayList, beginTime, endTime);
            float b = (float) (Math.round(a * 100)) / 100;
            float workHours = (float) (b - holidayHous + workDayHous);
            return workHours;
        }catch (ParseException parseException){
            parseException.printStackTrace();
            log.error("工作日计算报错信息",parseException.getMessage());
        }

        return  0;

    }



    /**
     * 计算开始时间和结束时间在调休或者节假日当天精确计算工作时长
     * @param dayList
     * @param beginTime
     * @param endTime
     * @return
     */
    private static double getRestDayHous(List<String> dayList,String beginTime,String endTime){
        String beginTimeDay=beginTime.substring(0,10);
        String endTimeDay=endTime.substring(0,10);
        double time=0;
        try{
            if(dayList.contains(beginTimeDay)||dayList.contains(endTimeDay)){
                if(beginTimeDay.equals(endTimeDay)){
                    time=getRestTime(beginTime,endTime);
                    return time;
                }

                double  sTime=0;
                double  eTime=0;
                CalculateHours ch = new CalculateHours();
                SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
                //开始时间跟上班时间比
                if(dayList.contains(beginTimeDay)){
                    Date t1 = ch.processBeginTime(sdf.parse(beginTime),1);
                    Date t2 = sdf.parse(beginTimeDay+" "+ch.beginWork);
                    Date t3 = sdf.parse(beginTimeDay+" "+ch.endWork);
                    if(t1.getTime()<=t2.getTime()){
                        sTime=9.5;
                    }else if(t1.getTime()>t2.getTime()){
                        float hh1 = t1.getHours() + (float)t1.getMinutes()/60;
                        float hh2 = t3.getHours() + (float)t3.getMinutes()/60;
                        float tt = 0;
                        tt = hh2 - hh1;
                        sTime=tt;
                    }
                }

                //结束时间跟下班时间比
                if(dayList.contains(endTimeDay)){
                    Date t1 = ch.processEndTime(sdf.parse(endTime),1);
                    Date t2 = sdf.parse(endTimeDay+" "+ch.beginWork);
                    Date t3 = sdf.parse(endTimeDay+" "+ch.endWork);
                    if(t1.getTime()>=t3.getTime()){
                        eTime=9.5;
                    }else if(t1.getTime()<t3.getTime()){
                        float hh1 = t1.getHours() + (float)t1.getMinutes()/60;
                        float hh2 = t2.getHours() + (float)t2.getMinutes()/60;
                        float tt = 0;
                        tt = hh1 - hh2;
                        eTime=tt;
                    }
                }


                if(sTime>0&&eTime>0){
                    time=(dayList.size()-2)*9.5+sTime+eTime;
                }else if(sTime>0&&eTime==0){
                    time=(dayList.size()-1)*9.5+sTime;
                }else if(sTime==0&&eTime>0){
                    time=(dayList.size()-1)*9.5+eTime;
                }


            }else{
                time=dayList.size()*9.5;
            }
        }catch (Exception e){

            e.printStackTrace();
        }



            return time;
    }

    /**
     * 获取调休日期工作时间
     * @param beginTime
     * @param endTime
     * @return
     */
    private static float getRestTime(String beginTime,String endTime){
        CalculateHours ch = new CalculateHours();
        //对输入的字符串形式的时间进行转换
        //真实开始时间
        Date t1 = ch.stringToDate(beginTime);
        //真实结束时间
        Date t2 = ch.stringToDate(endTime);
        //对时间进行预处理
        t1 = ch.processBeginTime(t1,1);
        t2 = ch.processEndTime(t2,1);


        float hh1 = t1.getHours() + (float)t1.getMinutes()/60 + (float)t1.getSeconds()/3600;
        float hh2 = t2.getHours() + (float)t2.getMinutes()/60 + (float)t1.getSeconds()/3600;

        float tt = 0;
        tt = hh2 - hh1;
        if(hh1>=ch.h1&&hh1<=ch.h2&&hh2>=ch.h3){
            tt = tt - (ch.h3-ch.h2);
        }
        return  tt;
    }

    /**
     * 去除数组中相同日期
     * @param arr1
     * @param arr2
     * @return
     */
    private static int arrContrast(String[] arr1, String[] arr2){
        int count=0;
        List<String> list = new LinkedList<String>();
        //处理第一个数组,list里面的值为1,2,3,4
        for (String str : arr1) {
            if (!list.contains(str)) {
                list.add(str);
            }
        }
        //如果第二个数组存在和第一个数组相同的值,就删除
        for (String str : arr2) {
            if(list.contains(str)){
                list.remove(str);
                ++count;
            }
        }
        return count;
    }

    private static List<String> arrContrastRest(String[] arr1, String[] arr2){
        List<String> list = new LinkedList<String>();
        List<String> restDayList = new LinkedList<String>();
        for (String str : arr1) {
            if (!list.contains(str)) {
                list.add(str);
            }
        }
        for (String str : arr2) {
            if(list.contains(str)){
                restDayList.add(str);
                list.remove(str);
            }
        }
        return restDayList;
    }


    private static final DateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 获取工作日
     * @param startDay
     * @param endDay
     * @return
     */
    private static String[] printDay(Calendar startDay, Calendar endDay) {
        List<String> list = new ArrayList<String>();
        // 给出的日期开始日比终了日大则不执行打印
        if (startDay.compareTo(endDay) > 0) {
            return new String[]{};
        }else if(startDay.compareTo(endDay) == 0){
            list.add(FORMATTER.format(startDay.getTime()));
            return list.toArray(new String[0]);
        }
        // 现在打印中的日期
        Calendar currentPrintDay = startDay;
        if(!getIsWeekend(currentPrintDay.getTime())){
            list.add(FORMATTER.format(currentPrintDay.getTime()));
        }
        while (true) {
            // 日期加一
            currentPrintDay.add(Calendar.DATE, 1);
            if(!getIsWeekend(currentPrintDay.getTime())) {
                list.add(FORMATTER.format(currentPrintDay.getTime()));
            }
            // 日期加一后判断是否达到终了日,达到则终止打印
            if (currentPrintDay.compareTo(endDay) == 0) {
                break;
            }
        }
        String[] arr = list.toArray(new String[0]);
        return arr;
    }


    /**
     * 获取自然日
     * @param startDay
     * @param endDay
     * @return
     */
    private static String[] printNaturalDay(Calendar startDay, Calendar endDay) {
        List<String> list = new ArrayList<String>();
        // 给出的日期开始日比终了日大则不执行打印
        if (startDay.compareTo(endDay) > 0) {
            return new String[]{};
        }else if(startDay.compareTo(endDay) == 0){
            list.add(FORMATTER.format(startDay.getTime()));
            return list.toArray(new String[0]);
        }
        // 现在打印中的日期
        Calendar currentPrintDay = startDay;
        list.add(FORMATTER.format(currentPrintDay.getTime()));

        while (true) {
            // 日期加一
            currentPrintDay.add(Calendar.DATE, 1);
            list.add(FORMATTER.format(currentPrintDay.getTime()));
            // 日期加一后判断是否达到终了日,达到则终止打印
            if (currentPrintDay.compareTo(endDay) == 0) {
                break;
            }
        }
        String[] arr = list.toArray(new String[0]);
        return arr;
    }

    /**
     * 判断是否周末
     * @param date
     * @return
     */
    private static boolean getIsWeekend(Date date){
        Week week= DateUtil.dayOfWeekEnum(date);
        return (Week.SUNDAY.equals(week)||Week.SATURDAY.equals(week));

    }
}