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

namespace JiShe.CollectBus.Common.Extensions
{
    public static class DateTimeExtensions
    {
        /// <summary>
        /// Converts a DateTime to a Unix Timestamp
        /// </summary>
        /// <param name="target">This DateTime</param>
        /// <returns></returns>
        [Description("将日期时间转换为Unix时间戳")]
        public static double ToUnixTimestamp(this DateTime target)
        {
            var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            var diff = target - origin;
            return Math.Floor(diff.TotalSeconds);
        }

        /// <summary>
        /// Converts a Unix Timestamp in to a DateTime
        /// </summary>
        /// <param name="unixTime">This Unix Timestamp</param>
        /// <returns></returns>
        [Description("将Unix时间戳转换为日期时间")]
        public static DateTime FromUnixTimestamp(this double unixTime)
        {
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return epoch.AddSeconds(unixTime);
        }

        /// <summary>
        /// Gets the value of the End of the day (23:59)
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        [Description("获取一天结束的值（23:59）")]
        public static DateTime ToDayEnd(this DateTime target)
        {
            return target.Date.AddDays(1).AddMilliseconds(-1);
        }

        /// <summary>
        /// Gets the First Date of the week for the specified date
        /// </summary>
        /// <param name="dt">this DateTime</param>
        /// <param name="startOfWeek">The Start Day of the Week (ie, Sunday/Monday)</param>
        /// <returns>The First Date of the week</returns>
        [Description("获取指定日期的星期的第一个日期")]
        public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
        {
            var diff = dt.DayOfWeek - startOfWeek;

            if (diff < 0)
                diff += 7;

            return dt.AddDays(-1 * diff).Date;
        }

        /// <summary>
        /// Returns all the days of a month.
        /// </summary>
        /// <param name="year">The year.</param>
        /// <param name="month">The month.</param>
        /// <returns></returns>
        [Description("获取一个月的所有日期")]
        public static IEnumerable<DateTime> DaysOfMonth(int year, int month)
        {
            return Enumerable.Range(0, DateTime.DaysInMonth(year, month))
                .Select(day => new DateTime(year, month, day + 1));
        }

        /// <summary>
        /// Determines the Nth instance of a Date's DayOfWeek in a month
        /// </summary>
        /// <returns></returns>
        /// <example>11/29/2011 would return 5, because it is the 5th Tuesday of each month</example>
        [Description("获取当前日期在一个月的第几个星期")]
        public static int WeekDayInstanceOfMonth(this DateTime dateTime)
        {
            var y = 0;
            return DaysOfMonth(dateTime.Year, dateTime.Month)
                .Where(date => dateTime.DayOfWeek.Equals(date.DayOfWeek))
                .Select(x => new { n = ++y, date = x })
                .Where(x => x.date.Equals(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day)))
                .Select(x => x.n).FirstOrDefault();
        }

        /// <summary>
        /// Gets the total days in a month
        /// </summary>
        /// <param name="dateTime">The date time.</param>
        /// <returns></returns>
        [Description("获取一个月内的总天数")]
        public static int TotalDaysInMonth(this DateTime dateTime)
        {
            return DaysOfMonth(dateTime.Year, dateTime.Month).Count();
        }

        /// <summary>
        /// Get the first day in a month
        /// </summary>
        /// <param name="dateTime">The date time.</param>
        /// <returns></returns>
        [Description("获取一个月内的第一天")]
        public static DateTime FirstInMonth(this DateTime dateTime)
        {
            return DateTime.Now.AddDays(1 - DateTime.Now.Day);
        }

        /// <summary>
        /// Get the Last Day in a Month 23:59:59
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        [Description("获取一个月内的最后一天 23:59:59")]
        public static DateTime LastInMonth(this DateTime dateTime)
        {
            return DateTime.Now.AddDays(1 - DateTime.Now.Day).Date.AddMonths(1).AddSeconds(-1);
        }

        /// <summary>
        /// Takes any date and returns it's value as an Unspecified DateTime
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        [Description("获取Unspecified日期")]
        public static DateTime ToDateTimeUnspecified(this DateTime date)
        {
            if (date.Kind == DateTimeKind.Unspecified)
            {
                return date;
            }

            return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Unspecified);
        }

        /// <summary>
        /// Trims the milliseconds off of a datetime
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        [Description("将日期时间缩短毫秒")]
        public static DateTime TrimMilliseconds(this DateTime date)
        {
            return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);
        }

        /// <summary>
        /// Clears the time.
        /// </summary>
        /// <param name="dateTime">The date time.</param>
        /// <returns></returns>
        [Description("清除时间")]
        public static DateTime ClearTime(this DateTime dateTime)
        {
            return dateTime.Subtract(
                new TimeSpan(
                    0,
                    dateTime.Hour,
                    dateTime.Minute,
                    dateTime.Second,
                    dateTime.Millisecond
                )
            );
        }

        /// <summary>
        /// 获取数据表分片策略
        /// </summary>
        /// <param name="dateTime"></param>
        /// <returns></returns>
        public static string GetDataTableShardingStrategy(this DateTime dateTime)
        {
#if DEBUG
            return $"{dateTime:yyyyMMddHHmm}";
#else
            return $"{dateTime:yyyyMMddHH}";
#endif
        }
         

        /// <summary>
        /// 采集时间节点计算
        /// </summary>
        /// <param name="referenceTime">待采集时间</param>
        /// <param name="interval"></param>
        /// <returns></returns>
        public static DateTime CalculateNextCollectionTime(this DateTime referenceTime, int interval)
        {
            // 计算精确到分钟的基准时间
            var baseTime = new DateTime(
                referenceTime.Year,
                referenceTime.Month,
                referenceTime.Day,
                referenceTime.Hour,
                referenceTime.Minute,
                0);

            // 计算总分钟数和下一个间隔点
            int totalMinutes = baseTime.Hour * 60 + baseTime.Minute;
            int nextTotalMinutes = ((totalMinutes / interval) + 1) * interval;

            // 处理跨天情况
            int daysToAdd = nextTotalMinutes / (24 * 60);
            int remainingMinutes = nextTotalMinutes % (24 * 60);
            int hours = remainingMinutes / 60;
            int minutes = remainingMinutes % 60;

            return baseTime.Date
                .AddDays(daysToAdd)
                .AddHours(hours)
                .AddMinutes(minutes);
        }


        /// <summary>
        /// 格式化为微秒（μs）
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static string ToMicrosecondString(this DateTime dt)
        {
            long microseconds = (dt.Ticks % TimeSpan.TicksPerSecond) / 10; // 1 Tick = 100ns → 0.1μs
            return $"{dt:yyyy-MM-dd HH:mm:ss.fffffff}".Remove(23) + $"{microseconds:D6}";
        }

        /// <summary>
        /// 格式化为纳秒（ns）
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static string ToNanosecondString(this DateTime dt)
        {
            long nanoseconds = (dt.Ticks % TimeSpan.TicksPerSecond) * 100; // 1 Tick = 100ns
            return $"{dt:yyyy-MM-dd HH:mm:ss.fffffff}".Remove(23) + $"{nanoseconds:D9}";
        }

        /// <summary>
        /// 毫米、微秒、纳秒时间戳转DateTime
        /// </summary>
        /// <param name="dateLong"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static DateTime ParseIntToDate(long dateLong)
        {
            if (dateLong < 10000101 || dateLong > 99991231)
            {
                throw new ArgumentException("Date must be between 10000101 and 99991231.");
            }
            return DateTime.TryParseExact(dateLong.ToString(), "yyyyMMdd HHmmssZZ", null, System.Globalization.DateTimeStyles.None, out DateTime date) ? date : throw new ArgumentException("Date must be between 10000101 and 99991231.");
        }
    }
}
