using System.Linq.Expressions;

namespace PARR.API.Helpers
{
    /// <summary>
    /// Хелпер для работы с датами в разных часовых поясах (UTC - пользователь). Используется для формирования СТАТИСТИКИ
    /// </summary>
    public static class TimeZoneDateHelper
    {
        /// <summary>
        /// Создаёт начало дня (00:00:00) в часовом поясе пользователя и конвертирует в UTC
        /// </summary>
        /// <param name="userDate"></param>
        /// <param name="userOffset"></param>
        /// <returns></returns>
        public static DateTime GetUtcSartOfDay(DateOnly userDate, TimeSpan userOffset)
        {
            var userStart = new DateTimeOffset(userDate.Year, userDate.Month, userDate.Day,
                                                    0, 0, 0,
                                                    userOffset);

            return userStart.UtcDateTime;
        }


        /// <summary>
        /// Создаёт полуоткрытый интервал [start; end) в UTC для фильтрации за период (end не включается)
        /// </summary>
        /// <param name="userStartDate"></param>
        /// <param name="userEndDateExclusive"></param>
        /// <param name="userOffset"></param>
        /// <returns></returns>
        public static (DateTime StartUtc, DateTime EndUtc) GetUtcDateRange(DateOnly userStartDate, DateOnly userEndDateExclusive, TimeSpan userOffset)
        {
            var start = new DateTimeOffset(
                               userStartDate.Year, userStartDate.Month, userStartDate.Day,
                               0, 0, 0, userOffset).UtcDateTime;

            var end = new DateTimeOffset(
                                userEndDateExclusive.Year, userEndDateExclusive.Month, userEndDateExclusive.Day,
                                0, 0, 0, userOffset).UtcDateTime;

            return (start, end);
        }


        /// <summary>
        /// Применяет фильтр по дате к IQueryable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="dateSelector"></param>
        /// <param name="startUtc"></param>
        /// <param name="endUtc"></param>
        /// <returns></returns>
        public static IQueryable<T> FilterByDateRangeUtc<T>(this IQueryable<T> query, Expression<Func<T, DateTimeOffset>> dateSelector, DateTime startUtc, DateTime endUtc)
        {
            // Строим выражение: t => t.DateCreated.UtcDateTime >= start && < end
            var param = dateSelector.Parameters[0];
            var body = Expression.AndAlso(
                    // Строим левую часть: t.DateCreated.UtcDateTime >= startUtc
                    Expression.GreaterThanOrEqual(
                            Expression.Property(dateSelector.Body, nameof(DateTimeOffset.UtcDateTime)),
                            Expression.Constant(startUtc, typeof(DateTime))
                        ),
                    // Строим правую часть: t.DateCreated.UtcDateTime < endUtc
                    Expression.LessThan(
                            Expression.Property(dateSelector.Body, nameof(DateTimeOffset.UtcDateTime)),
                            Expression.Constant(endUtc, typeof(DateTime))
                        )
                );

            //  Собираем финальную лямбду: t => (t.DateCreated.UtcDateTime >= start && < end)
            var lambda = Expression.Lambda<Func<T, bool>>(body, param);

            // Применяем к запросу
            return query.Where(lambda);
        }


        /// <summary>
        /// Группирует записи по дате в часовом поясе пользователя (для загруженных записей)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="dateSelector"></param>
        /// <param name="userOffset"></param>
        /// <returns></returns>
        public static Dictionary<DateOnly, int> GroupByUserDate<T>(this IEnumerable<T> source, Func<T, DateTimeOffset> dateSelector, TimeSpan userOffset)
        {
            return source.GroupBy(t => DateOnly.FromDateTime(dateSelector(t).ToOffset(userOffset).DateTime))
                            .ToDictionary(g => g.Key, g => g.Count());

        }
    }
}
