package shop.alien.lawyer.util;

import com.nlf.calendar.Holiday;
import com.nlf.calendar.util.HolidayUtil;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 日期计算工具类
 *
 * @author ssk
 * @version 1.0
 * @date 2023/5/24 16:03
 */
public class DateUtils {

    /**
     * 天数加减
     *
     * @param date 日期
     * @param days 加减天数
     * @return 处理后的日期
     */
    public static Date calcDays(Date date, Integer days) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.DAY_OF_MONTH, days);
        return c.getTime();
    }

    /**
     * 小时加减
     *
     * @param date  日期
     * @param hours 加减时间
     * @return 处理后的日期
     */
    public static Date calcHours(Date date, Integer hours) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.HOUR_OF_DAY, hours);
        return c.getTime();
    }

    /**
     * 分钟加减
     *
     * @param date   日期
     * @param minute 加减时间
     * @return 处理后的日期
     */
    public static Date calcMinute(Date date, Integer minute) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.MINUTE, minute);
        return c.getTime();
    }

    /**
     * 日期比较
     *
     * @param dateOne 日期1
     * @param dateTwo 日期2
     * @return 是否大于
     */
    public static boolean dateCompare(Date dateOne, Date dateTwo) {
        return dateOne.compareTo(dateTwo) > 0;
    }

    /**
     * 今日剩余秒数
     *
     * @return int
     */
    public static Integer todayLastSecond() {
        int daySecond = 86400;
        LocalDateTime now = LocalDateTime.now();
        int hour = now.getHour() * 60 * 60;
        int minute = now.getMinute() * 60;
        int second = now.getSecond();
        return daySecond - hour - minute - second;
    }

    /**
     * 工作日信息类
     */
    public static class WorkdayInfo {
        private LocalDateTime startTime;
        private LocalDateTime endTime;

        public WorkdayInfo(LocalDateTime startTime, LocalDateTime endTime) {
            this.startTime = startTime;
            this.endTime = endTime;
        }

        public LocalDateTime getStartTime() {
            return startTime;
        }

        public void setStartTime(LocalDateTime startTime) {
            this.startTime = startTime;
        }

        public LocalDateTime getEndTime() {
            return endTime;
        }

        public void setEndTime(LocalDateTime endTime) {
            this.endTime = endTime;
        }
    }

    /**
     * 判断是否为工作日（考虑周末和法定节假日）
     * 
     * @param date 日期
     * @return 是否为工作日
     */
    private static boolean isWorkday(LocalDate date) {
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        
        // 如果是周末（周六或周日），直接返回 false
        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
            return false;
        }
        
        // 通过 HolidayUtil 判断是否为法定节假日
        Holiday holiday = HolidayUtil.getHoliday(date.getYear(), date.getMonthValue(), date.getDayOfMonth());
        
        // 如果没有节假日信息，按照周一到周五判断为工作日
        if (holiday == null) {
            return true;
        }
        
        // 使用节假日信息中的 isWork() 方法判断
        // true 表示工作日（包括调休工作日），false 表示节假日
        return holiday.isWork();
    }

    /**
     * 获取当月最后三个工作日的开始时间点和结束时间点
     * 工作日定义为周一到周五，且排除周末和法定节假日（包括调休工作日）
     *
     * @return 最后三个工作日的时间信息列表，按时间从早到晚排序（第一个是最早的，第三个是最晚的）
     */
    public static List<WorkdayInfo> getLastThreeWorkdaysOfCurrentMonth(int day) {
        LocalDate now = LocalDate.now();
        // 获取当月最后一天
        LocalDate lastDayOfMonth = now.withDayOfMonth(now.lengthOfMonth());
        
        List<WorkdayInfo> workdayList = new ArrayList<>();
        
        // 从最后一天往前遍历，找到工作日
        LocalDate currentDate = lastDayOfMonth;
        while (workdayList.size() < day && currentDate.getMonthValue() == now.getMonthValue()) {
            if (isWorkday(currentDate)) {
                // 开始时间：当天的 00:00:00
                LocalDateTime startTime = LocalDateTime.of(currentDate, LocalTime.MIN);
                // 结束时间：当天的 23:59:59.999999999，或者使用第二天的 00:00:00（不包含）
                // 这里使用当天的 23:59:59.999999999
                LocalDateTime endTime = LocalDateTime.of(currentDate, LocalTime.MAX);
                workdayList.add(0, new WorkdayInfo(startTime, endTime)); // 插入到列表开头，保持时间顺序
            }
            currentDate = currentDate.minusDays(1);
        }
        
        return workdayList;
    }

    /**
     * 获取当月最后三个工作日的时间范围
     * 返回第一个工作日的开始时间和本月最后一天的最晚时间，格式为 yyyy-MM-dd HH:mm:ss
     * 工作日定义为周一到周五，且排除周末和法定节假日（包括调休工作日）
     *
     * @return 包含开始时间和结束时间的Map，key为"startTime"和"endTime"，值为格式化的字符串
     */
    public static Map<String, String> getLastThreeWorkdaysRange(int day) {
        List<WorkdayInfo> workdays = getLastThreeWorkdaysOfCurrentMonth(day);
        Map<String, String> result = new HashMap<>();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        if (workdays.isEmpty()) {
            result.put("startTime", null);
            result.put("endTime", null);
            return result;
        }
        
        // 第一个工作日（最早的那个）的开始时间
        WorkdayInfo firstWorkday = workdays.get(0);
        String startTime = firstWorkday.getStartTime().format(formatter);
        
        // 本月最后一天的最晚时间（23:59:59）
        LocalDate now = LocalDate.now();
        LocalDate lastDayOfMonth = now.withDayOfMonth(now.lengthOfMonth());
        LocalDateTime endDateTime = LocalDateTime.of(lastDayOfMonth, LocalTime.MAX);
        String endTime = endDateTime.format(formatter);
        
        result.put("startTime", startTime);
        result.put("endTime", endTime);
        
        return result;
    }

    /**
     * 判断当前时间是否在当月最后几个工作日的时间范围内
     * 根据 getLastThreeWorkdaysRangeInfo 方法的逻辑，判断当前时间是否在返回的时间范围内
     * 工作日定义为周一到周五，且排除周末和法定节假日（包括调休工作日）
     *
     * @param day 工作日天数
     * @return true 表示当前时间在时间范围内（包含边界），false 表示不在范围内
     */
    public static boolean getLastThreeWorkdaysRangeInfo(int day) {
        List<WorkdayInfo> workdays = getLastThreeWorkdaysOfCurrentMonth(day);
        
        if (workdays.isEmpty()) {
            return false;
        }
        
        // 第一个工作日（最早的那个）的开始时间
        WorkdayInfo firstWorkday = workdays.get(0);
        LocalDateTime startDateTime = firstWorkday.getStartTime();
        
        // 本月最后一天的最晚时间（23:59:59）
        LocalDate now = LocalDate.now();
        LocalDate lastDayOfMonth = now.withDayOfMonth(now.lengthOfMonth());
        LocalDateTime endDateTime = LocalDateTime.of(lastDayOfMonth, LocalTime.MAX);
        
        // 判断当前时间是否在范围内（包含边界）
        LocalDateTime currentTime = LocalDateTime.now();
        return !currentTime.isBefore(startDateTime) && !currentTime.isAfter(endDateTime);
    }
}
