using System;
using System.Collections.Generic;
using System.Linq;

namespace NPP.SmartSchedue.Api.Contracts.Domain.Common
{
    /// <summary>
    /// 日期范围类
    /// 智能排班系统中的时间区间管理，支持工作日计算、工时统计和时间跨度分析
    /// 深度业务思考：提供完整的时间范围操作，支持复杂的排班业务场景
    /// </summary>
    public class DateRange
    {
        #region 属性定义

        /// <summary>
        /// 开始日期
        /// </summary>
        public DateTime StartDate { get; set; }

        /// <summary>
        /// 结束日期
        /// </summary>
        public DateTime EndDate { get; set; }

        /// <summary>
        /// 时间跨度
        /// 计算开始日期和结束日期之间的时间差
        /// </summary>
        public TimeSpan Duration => EndDate.Date - StartDate.Date;

        /// <summary>
        /// 总天数（包含开始和结束日期）
        /// </summary>
        public int TotalDays => Duration.Days + 1;

        /// <summary>
        /// 纯粹的天数差（不包含结束日期）
        /// </summary>
        public int DurationDays => Duration.Days;

        /// <summary>
        /// 工作日天数
        /// 排除周末和节假日的实际工作天数
        /// </summary>
        public int WorkingDays => CalculateWorkingDays();

        /// <summary>
        /// 自然工作日天数
        /// 仅排除周末，不考虑节假日
        /// </summary>
        public int NaturalWorkingDays => CalculateNaturalWorkingDays();

        /// <summary>
        /// 周末天数
        /// </summary>
        public int WeekendDays => CalculateWeekendDays();

        /// <summary>
        /// 节假日天数
        /// </summary>
        public int HolidayDays => CalculateHolidayDays();

        /// <summary>
        /// 是否为有效的日期范围
        /// </summary>
        public bool IsValid => StartDate <= EndDate;

        /// <summary>
        /// 是否为单日范围
        /// </summary>
        public bool IsSingleDay => StartDate.Date == EndDate.Date;

        /// <summary>
        /// 是否跨越多个月份
        /// </summary>
        public bool SpansMultipleMonths => StartDate.Month != EndDate.Month || StartDate.Year != EndDate.Year;

        /// <summary>
        /// 是否跨越多个年份
        /// </summary>
        public bool SpansMultipleYears => StartDate.Year != EndDate.Year;

        #endregion

        #region 构造函数

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public DateRange()
        {
            StartDate = DateTime.Today;
            EndDate = DateTime.Today;
        }

        /// <summary>
        /// 指定开始和结束日期的构造函数
        /// </summary>
        /// <param name="startDate">开始日期</param>
        /// <param name="endDate">结束日期</param>
        public DateRange(DateTime startDate, DateTime endDate)
        {
            StartDate = startDate.Date;
            EndDate = endDate.Date;
            
            if (!IsValid)
            {
                throw new ArgumentException("结束日期不能早于开始日期");
            }
        }

        /// <summary>
        /// 从指定日期开始，持续指定天数的构造函数
        /// </summary>
        /// <param name="startDate">开始日期</param>
        /// <param name="days">持续天数</param>
        public DateRange(DateTime startDate, int days)
        {
            if (days < 0)
            {
                throw new ArgumentException("天数不能为负数");
            }
            
            StartDate = startDate.Date;
            EndDate = startDate.Date.AddDays(days - 1);
        }

        #endregion

        #region 工时计算方法

        /// <summary>
        /// 计算标准工时
        /// 基于每天8小时的标准工作时间
        /// </summary>
        /// <param name="dailyStandardHours">每日标准工时，默认8小时</param>
        /// <returns>总标准工时</returns>
        public decimal CalculateStandardHours(decimal dailyStandardHours = 8.0m)
        {
            return WorkingDays * dailyStandardHours;
        }

        /// <summary>
        /// 计算自然日标准工时
        /// 基于总天数计算，不考虑工作日
        /// </summary>
        /// <param name="dailyStandardHours">每日标准工时，默认8小时</param>
        /// <returns>总标准工时</returns>
        public decimal CalculateNaturalStandardHours(decimal dailyStandardHours = 8.0m)
        {
            return TotalDays * dailyStandardHours;
        }

        /// <summary>
        /// 计算工作日工时
        /// 仅计算工作日的工时，排除周末和节假日
        /// </summary>
        /// <param name="dailyWorkingHours">每个工作日的工时，默认8小时</param>
        /// <returns>工作日总工时</returns>
        public decimal CalculateWorkingHours(decimal dailyWorkingHours = 8.0m)
        {
            return WorkingDays * dailyWorkingHours;
        }

        /// <summary>
        /// 计算可用工时
        /// 考虑具体的工作时间配置
        /// </summary>
        /// <param name="workingTimeConfig">工作时间配置</param>
        /// <returns>可用总工时</returns>
        public decimal CalculateAvailableHours(WorkingTimeConfiguration workingTimeConfig = null)
        {
            if (workingTimeConfig == null)
            {
                return CalculateWorkingHours();
            }

            decimal totalHours = 0;
            var currentDate = StartDate;

            while (currentDate <= EndDate)
            {
                if (IsWorkingDay(currentDate))
                {
                    totalHours += workingTimeConfig.GetDailyWorkingHours(currentDate);
                }
                currentDate = currentDate.AddDays(1);
            }

            return totalHours;
        }

        #endregion

        #region 日期判断方法

        /// <summary>
        /// 判断指定日期是否在范围内
        /// </summary>
        /// <param name="date">要判断的日期</param>
        /// <returns>是否在范围内</returns>
        public bool Contains(DateTime date)
        {
            var checkDate = date.Date;
            return checkDate >= StartDate && checkDate <= EndDate;
        }

        /// <summary>
        /// 判断是否与另一个日期范围重叠
        /// </summary>
        /// <param name="other">另一个日期范围</param>
        /// <returns>是否重叠</returns>
        public bool Overlaps(DateRange other)
        {
            if (other == null) return false;
            return StartDate <= other.EndDate && EndDate >= other.StartDate;
        }

        /// <summary>
        /// 判断是否完全包含另一个日期范围
        /// </summary>
        /// <param name="other">另一个日期范围</param>
        /// <returns>是否完全包含</returns>
        public bool Contains(DateRange other)
        {
            if (other == null) return false;
            return StartDate <= other.StartDate && EndDate >= other.EndDate;
        }

        /// <summary>
        /// 判断指定日期是否为工作日
        /// </summary>
        /// <param name="date">要判断的日期</param>
        /// <returns>是否为工作日</returns>
        public bool IsWorkingDay(DateTime date)
        {
            // 排除周末
            if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
            {
                return false;
            }

            // 排除节假日
            return !IsHoliday(date);
        }

        /// <summary>
        /// 判断指定日期是否为节假日
        /// </summary>
        /// <param name="date">要判断的日期</param>
        /// <returns>是否为节假日</returns>
        public bool IsHoliday(DateTime date)
        {
            // 这里可以集成具体的节假日数据源
            // 暂时返回false，实际项目中需要根据具体需求实现
            return HolidayProvider.IsHoliday(date);
        }

        #endregion

        #region 范围操作方法

        /// <summary>
        /// 获取与另一个日期范围的交集
        /// </summary>
        /// <param name="other">另一个日期范围</param>
        /// <returns>交集范围，如果没有交集则返回null</returns>
        public DateRange Intersect(DateRange other)
        {
            if (other == null || !Overlaps(other))
            {
                return null;
            }

            var intersectStart = StartDate > other.StartDate ? StartDate : other.StartDate;
            var intersectEnd = EndDate < other.EndDate ? EndDate : other.EndDate;

            return new DateRange(intersectStart, intersectEnd);
        }

        /// <summary>
        /// 获取与另一个日期范围的并集
        /// </summary>
        /// <param name="other">另一个日期范围</param>
        /// <returns>并集范围</returns>
        public DateRange Union(DateRange other)
        {
            if (other == null)
            {
                return new DateRange(StartDate, EndDate);
            }

            var unionStart = StartDate < other.StartDate ? StartDate : other.StartDate;
            var unionEnd = EndDate > other.EndDate ? EndDate : other.EndDate;

            return new DateRange(unionStart, unionEnd);
        }

        /// <summary>
        /// 扩展日期范围
        /// </summary>
        /// <param name="daysBefore">向前扩展的天数</param>
        /// <param name="daysAfter">向后扩展的天数</param>
        /// <returns>扩展后的日期范围</returns>
        public DateRange Extend(int daysBefore, int daysAfter)
        {
            return new DateRange(
                StartDate.AddDays(-daysBefore),
                EndDate.AddDays(daysAfter)
            );
        }

        /// <summary>
        /// 拆分日期范围为指定天数的子范围
        /// </summary>
        /// <param name="chunkSize">每个子范围的天数</param>
        /// <returns>子范围列表</returns>
        public List<DateRange> Split(int chunkSize)
        {
            if (chunkSize <= 0)
            {
                throw new ArgumentException("块大小必须大于0");
            }

            var chunks = new List<DateRange>();
            var currentStart = StartDate;

            while (currentStart <= EndDate)
            {
                var currentEnd = currentStart.AddDays(chunkSize - 1);
                if (currentEnd > EndDate)
                {
                    currentEnd = EndDate;
                }

                chunks.Add(new DateRange(currentStart, currentEnd));
                currentStart = currentEnd.AddDays(1);
            }

            return chunks;
        }

        #endregion

        #region 日期枚举方法

        /// <summary>
        /// 获取范围内的所有日期
        /// </summary>
        /// <returns>日期列表</returns>
        public List<DateTime> GetAllDates()
        {
            var dates = new List<DateTime>();
            var currentDate = StartDate;

            while (currentDate <= EndDate)
            {
                dates.Add(currentDate);
                currentDate = currentDate.AddDays(1);
            }

            return dates;
        }

        /// <summary>
        /// 获取范围内的所有工作日
        /// </summary>
        /// <returns>工作日列表</returns>
        public List<DateTime> GetWorkingDays()
        {
            return GetAllDates().Where(IsWorkingDay).ToList();
        }

        /// <summary>
        /// 获取范围内的所有周末
        /// </summary>
        /// <returns>周末日期列表</returns>
        public List<DateTime> GetWeekends()
        {
            return GetAllDates().Where(date => 
                date.DayOfWeek == DayOfWeek.Saturday || 
                date.DayOfWeek == DayOfWeek.Sunday).ToList();
        }

        /// <summary>
        /// 获取范围内的所有节假日
        /// </summary>
        /// <returns>节假日列表</returns>
        public List<DateTime> GetHolidays()
        {
            return GetAllDates().Where(IsHoliday).ToList();
        }

        #endregion

        #region 私有计算方法

        /// <summary>
        /// 计算工作日天数
        /// </summary>
        private int CalculateWorkingDays()
        {
            return GetWorkingDays().Count;
        }

        /// <summary>
        /// 计算自然工作日天数（仅排除周末）
        /// </summary>
        private int CalculateNaturalWorkingDays()
        {
            var workDays = 0;
            var currentDate = StartDate;

            while (currentDate <= EndDate)
            {
                if (currentDate.DayOfWeek != DayOfWeek.Saturday && 
                    currentDate.DayOfWeek != DayOfWeek.Sunday)
                {
                    workDays++;
                }
                currentDate = currentDate.AddDays(1);
            }

            return workDays;
        }

        /// <summary>
        /// 计算周末天数
        /// </summary>
        private int CalculateWeekendDays()
        {
            return GetWeekends().Count;
        }

        /// <summary>
        /// 计算节假日天数
        /// </summary>
        private int CalculateHolidayDays()
        {
            return GetHolidays().Count;
        }

        #endregion

        #region 静态工厂方法

        /// <summary>
        /// 创建今天的日期范围
        /// </summary>
        public static DateRange Today()
        {
            return new DateRange(DateTime.Today, DateTime.Today);
        }

        /// <summary>
        /// 创建本周的日期范围
        /// </summary>
        public static DateRange ThisWeek()
        {
            var today = DateTime.Today;
            var startOfWeek = today.AddDays(-(int)today.DayOfWeek);
            var endOfWeek = startOfWeek.AddDays(6);
            return new DateRange(startOfWeek, endOfWeek);
        }

        /// <summary>
        /// 创建本月的日期范围
        /// </summary>
        public static DateRange ThisMonth()
        {
            var today = DateTime.Today;
            var startOfMonth = new DateTime(today.Year, today.Month, 1);
            var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
            return new DateRange(startOfMonth, endOfMonth);
        }

        /// <summary>
        /// 创建本年的日期范围
        /// </summary>
        public static DateRange ThisYear()
        {
            var today = DateTime.Today;
            var startOfYear = new DateTime(today.Year, 1, 1);
            var endOfYear = new DateTime(today.Year, 12, 31);
            return new DateRange(startOfYear, endOfYear);
        }

        /// <summary>
        /// 创建指定月份的日期范围
        /// </summary>
        public static DateRange ForMonth(int year, int month)
        {
            var startOfMonth = new DateTime(year, month, 1);
            var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
            return new DateRange(startOfMonth, endOfMonth);
        }

        /// <summary>
        /// 创建指定年份的日期范围
        /// </summary>
        public static DateRange ForYear(int year)
        {
            var startOfYear = new DateTime(year, 1, 1);
            var endOfYear = new DateTime(year, 12, 31);
            return new DateRange(startOfYear, endOfYear);
        }

        #endregion

        #region 重写方法

        /// <summary>
        /// 重写ToString方法
        /// </summary>
        public override string ToString()
        {
            if (IsSingleDay)
            {
                return StartDate.ToString("yyyy-MM-dd");
            }
            return $"{StartDate:yyyy-MM-dd} ~ {EndDate:yyyy-MM-dd} ({TotalDays}天)";
        }

        /// <summary>
        /// 重写Equals方法
        /// </summary>
        public override bool Equals(object obj)
        {
            if (obj is DateRange other)
            {
                return StartDate == other.StartDate && EndDate == other.EndDate;
            }
            return false;
        }

        /// <summary>
        /// 重写GetHashCode方法
        /// </summary>
        public override int GetHashCode()
        {
            return HashCode.Combine(StartDate, EndDate);
        }

        #endregion
    }

    #region 支持类定义

    /// <summary>
    /// 工作时间配置
    /// </summary>
    public class WorkingTimeConfiguration
    {
        /// <summary>
        /// 标准每日工作时间
        /// </summary>
        public decimal StandardDailyHours { get; set; } = 8.0m;

        /// <summary>
        /// 特殊日期工作时间配置
        /// Key: 日期, Value: 工作时间
        /// </summary>
        public Dictionary<DateTime, decimal> SpecialDayHours { get; set; } = new();

        /// <summary>
        /// 获取指定日期的工作时间
        /// </summary>
        public decimal GetDailyWorkingHours(DateTime date)
        {
            return SpecialDayHours.GetValueOrDefault(date.Date, StandardDailyHours);
        }
    }

    /// <summary>
    /// 节假日提供者
    /// </summary>
    public static class HolidayProvider
    {
        /// <summary>
        /// 节假日缓存
        /// </summary>
        private static readonly HashSet<DateTime> _holidays = new();

        /// <summary>
        /// 判断是否为节假日
        /// </summary>
        public static bool IsHoliday(DateTime date)
        {
            // 这里可以集成具体的节假日数据源
            // 例如：国家法定节假日API、企业自定义节假日等
            return _holidays.Contains(date.Date);
        }

        /// <summary>
        /// 添加节假日
        /// </summary>
        public static void AddHoliday(DateTime date)
        {
            _holidays.Add(date.Date);
        }

        /// <summary>
        /// 批量添加节假日
        /// </summary>
        public static void AddHolidays(IEnumerable<DateTime> dates)
        {
            foreach (var date in dates)
            {
                _holidays.Add(date.Date);
            }
        }

        /// <summary>
        /// 移除节假日
        /// </summary>
        public static void RemoveHoliday(DateTime date)
        {
            _holidays.Remove(date.Date);
        }

        /// <summary>
        /// 清空所有节假日
        /// </summary>
        public static void ClearHolidays()
        {
            _holidays.Clear();
        }
    }

    #endregion
}