using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using Masuit.Tools.Models;
namespace Masuit.Tools.DateTimeExt
{
    /// 
    /// 日期操作工具类
    /// 
    public static class DateTimeHelper
    {
        /// 
        /// 获取某一年有多少周
        /// 
        /// 
        /// 该年周数
        public static int GetWeekAmount(this in DateTime now)
        {
            var end = new DateTime(now.Year, 12, 31); //该年最后一天
            var gc = new GregorianCalendar();
            return gc.GetWeekOfYear(end, CalendarWeekRule.FirstDay, DayOfWeek.Monday); //该年星期数
        }
        /// 
        /// 返回年度第几个星期   默认星期日是第一天
        /// 
        /// 时间
        /// 第几周
        public static int WeekOfYear(this in DateTime date)
        {
            var gc = new GregorianCalendar();
            return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
        }
        /// 
        /// 返回年度第几个星期
        /// 
        /// 时间
        /// 一周的开始日期
        /// 第几周
        public static int WeekOfYear(this in DateTime date, DayOfWeek week)
        {
            var gc = new GregorianCalendar();
            return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, week);
        }
        /// 
        /// 得到一年中的某周的起始日和截止日
        /// 周数 nNumWeek
        /// 
        /// 
        /// 第几周
        public static DateTimeRange GetWeekTime(this DateTime now, int nNumWeek)
        {
            var dt = new DateTime(now.Year, 1, 1);
            dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
            return new DateTimeRange(dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday), dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1));
        }
        #region P/Invoke 设置本地时间
        [DllImport("kernel32.dll")]
        private static extern bool SetLocalTime(ref SystemTime time);
        [StructLayout(LayoutKind.Sequential)]
        private record struct SystemTime
        {
            public short year;
            public short month;
            public short dayOfWeek;
            public short day;
            public short hour;
            public short minute;
            public short second;
            public short milliseconds;
        }
        /// 
        /// 设置本地计算机系统时间,仅支持Windows系统
        /// 
        /// DateTime对象
        public static void SetLocalTime(this in DateTime dt)
        {
            SystemTime st;
            st.year = (short)dt.Year;
            st.month = (short)dt.Month;
            st.dayOfWeek = (short)dt.DayOfWeek;
            st.day = (short)dt.Day;
            st.hour = (short)dt.Hour;
            st.minute = (short)dt.Minute;
            st.second = (short)dt.Second;
            st.milliseconds = (short)dt.Millisecond;
            SetLocalTime(ref st);
        }
        #endregion P/Invoke 设置本地时间
        /// 
        /// 返回相对于当前时间的相对天数
        /// 
        /// 
        /// 相对天数
        public static string GetDateTime(this in DateTime dt, int relativeday)
        {
            return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss");
        }
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的秒数
        /// 
        /// 
        /// 
        public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L;
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数
        /// 
        /// 
        /// 
        public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L;
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳
        /// 
        /// 
        /// 
        public static long GetTotalMicroseconds(this in DateTime dt) => (new DateTimeOffset(dt).UtcTicks - 621355968000000000) / 10;
        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳
        /// 
        /// 
        /// 
        public static long GetTotalNanoseconds(this in DateTime dt)
        {
            var ticks = (new DateTimeOffset(dt).UtcTicks - 621355968000000000) * 100;
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                QueryPerformanceCounter(out var timestamp);
                return ticks + timestamp % 100;
            }
            return ticks + Stopwatch.GetTimestamp() % 100;
        }
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的分钟数
        /// 
        /// 
        /// 
        public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes;
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的小时数
        /// 
        /// 
        /// 
        public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours;
        /// 
        /// 获取该时间相对于1970-01-01T00:00:00Z的天数
        /// 
        /// 
        /// 
        public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays;
        /// 本年有多少天
        /// 日期
        /// 本天在当年的天数
        public static int GetDaysOfYear(this in DateTime dt)
        {
            //取得传入参数的年份部分,用来判断是否是闰年
            int n = dt.Year;
            return DateTime.IsLeapYear(n) ? 366 : 365;
        }
        /// 本月有多少天
        /// 
        /// 天数
        public static int GetDaysOfMonth(this DateTime now)
        {
            return now.Month switch
            {
                1 => 31,
                2 => DateTime.IsLeapYear(now.Year) ? 29 : 28,
                3 => 31,
                4 => 30,
                5 => 31,
                6 => 30,
                7 => 31,
                8 => 31,
                9 => 30,
                10 => 31,
                11 => 30,
                12 => 31,
                _ => 0
            };
        }
        /// 返回当前日期的星期名称
        /// 日期
        /// 星期名称
        public static string GetWeekNameOfDay(this in DateTime now)
        {
            return now.DayOfWeek switch
            {
                DayOfWeek.Monday => "星期一",
                DayOfWeek.Tuesday => "星期二",
                DayOfWeek.Wednesday => "星期三",
                DayOfWeek.Thursday => "星期四",
                DayOfWeek.Friday => "星期五",
                DayOfWeek.Saturday => "星期六",
                DayOfWeek.Sunday => "星期日",
                _ => ""
            };
        }
        /// 
        /// 判断时间是否在区间内
        /// 
        /// 
        /// 开始
        /// 结束
        /// 模式
        /// 
        public static bool In(this in DateTime @this, DateTime start, DateTime end, RangeMode mode = RangeMode.Close)
        {
            return mode switch
            {
                RangeMode.Open => start < @this && end > @this,
                RangeMode.Close => start <= @this && end >= @this,
                RangeMode.OpenClose => start < @this && end >= @this,
                RangeMode.CloseOpen => start <= @this && end > @this,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
            };
        }
        /// 
        /// 返回某年某月最后一天
        /// 
        /// 
        /// 日
        public static int GetMonthLastDate(this DateTime now)
        {
            DateTime lastDay = new DateTime(now.Year, now.Month, new GregorianCalendar().GetDaysInMonth(now.Year, now.Month));
            return lastDay.Day;
        }
        /// 
        /// 获得一段时间内有多少小时
        /// 
        /// 起始时间
        /// 终止时间
        /// 小时差
        public static string GetTimeDelay(this in DateTime start, DateTime end)
        {
            return (end - start).ToString("c");
        }
        /// 
        /// 返回时间差
        /// 
        /// 时间1
        /// 时间2
        /// 时间差
        public static string DateDiff(this in DateTime dateTime1, in DateTime dateTime2)
        {
            string dateDiff;
            var ts = dateTime2 - dateTime1;
            if (ts.TotalDays >= 1)
            {
                dateDiff = ts.TotalDays >= 30 ? (ts.TotalDays / 30) + "个月前" : ts.TotalDays + "天前";
            }
            else
            {
                dateDiff = ts.Hours > 1 ? ts.Hours + "小时前" : ts.Minutes + "分钟前";
            }
            return dateDiff;
        }
        /// 
        /// 计算2个时间差
        /// 
        /// 开始时间
        /// 结束时间
        /// 时间差
        public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
        {
            string strResout = string.Empty;
            //获得2时间的时间间隔秒计算
            TimeSpan span = endTime.Subtract(beginTime);
            if (span.Days >= 365)
            {
                strResout += span.Days / 365 + "年";
            }
            if (span.Days >= 30)
            {
                strResout += span.Days % 365 / 30 + "个月";
            }
            if (span.Days >= 1)
            {
                strResout += (int)(span.TotalDays % 30.42) + "天";
            }
            if (span.Hours >= 1)
            {
                strResout += span.Hours + "小时";
            }
            if (span.Minutes >= 1)
            {
                strResout += span.Minutes + "分钟";
            }
            if (span.Seconds >= 1)
            {
                strResout += span.Seconds + "秒";
            }
            return strResout;
        }
        /// 
        /// 根据某个时间段查找在某批时间段中的最大并集
        /// 
        /// 
        /// 
        /// 
        /// 
        public static ICollection GetUnionSet(this T destination, List sources) where T : ITimePeriod, new()
        {
            var result = true;
            ICollection frames = new List();
            var timeFrames = sources.Where(frame =>
                !(destination.Start > frame.End || destination.End < frame.Start)).ToList();
            if (timeFrames.Any())
                foreach (var frame in timeFrames)
                {
                    frames.Add(frame);
                    sources.Remove(frame);
                }
            if (!frames.Any()) return frames;
            var timePeriod = new T()
            {
                End = frames.OrderBy(frame => frame.End).Max(frame => frame.End),
                Start = frames.OrderBy(frame => frame.Start).Min(frame => frame.Start)
            };
            while (result)
            {
                var maxTimeFrame = GetUnionSet(timePeriod, sources);
                if (!maxTimeFrame.Any())
                    result = false;
                else
                    foreach (var frame in maxTimeFrame)
                        frames.Add(frame);
            }
            return frames;
        }
        /// 
        /// 获取一批时间段内存在相互重叠的最大时间段
        /// 
        /// 基础时间段
        /// 一批时间段
        /// 
        /// 
        /// 源数据sources 会受到影响
        public static T GetMaxTimePeriod(this T destination, List sources) where T : ITimePeriod, new()
        {
            var list = sources.Select(period => new T()
            {
                End = period.End,
                Start = period.Start,
            }).ToList();
            var timePeriods = GetUnionSet(destination, list);
            return new T()
            {
                End = timePeriods.OrderBy(period => period.End).Max(period => period.End),
                Start = timePeriods.OrderBy(period => period.Start).Min(period => period.Start)
            };
        }
    }
    /// 
    ///
    /// 
    public interface ITimePeriod
    {
        /// 
        /// 起始时间
        /// 
        public DateTime Start { get; set; }
        /// 
        /// 终止时间
        /// 
        public DateTime End { get; set; }
    }
    /// 
    /// 区间模式
    /// 
    public enum RangeMode
    {
        /// 
        /// 开区间
        /// 
        Open,
        /// 
        /// 闭区间
        /// 
        Close,
        /// 
        /// 左开右闭区间
        /// 
        OpenClose,
        /// 
        /// 左闭右开区间
        /// 
        CloseOpen
    }
}