using Microsoft.Extensions.Logging;
using PARR.BLL.Services.Interfaces;
using PARR.Common;
using PARR.Constants;

namespace PARR.BLL.Services.Implementations
{
    internal class CalendarService : ICalendarService
    {
        private readonly ILogger<CalendarService> logger;

        public CalendarService(ILogger<CalendarService> logger)
        {
            this.logger = logger;
        }


        public DateOnly GetEndPeriodDate(DateOnly startPeriod, DistributionPeriodTypeEnum periodType, string distributionPeriod)
        {
            switch (periodType)
            {
                case (DistributionPeriodTypeEnum.Day):
                    return startPeriod.AddDays(ParseInt(distributionPeriod)).AddDays(-1);

                case (DistributionPeriodTypeEnum.Month):
                    return startPeriod.AddMonths(ParseInt(distributionPeriod)).AddDays(-1);

                case (DistributionPeriodTypeEnum.Year):
                    return startPeriod.AddYears(ParseInt(distributionPeriod)).AddDays(-1);

            }

            return startPeriod;
        }


        private int ParseInt(string value)
        {
            try
            {
                return int.Parse(value);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, $"{nameof(CalendarService)}, получение конца периода. Не смог распарсить string в int для значения {value}.");
                return 0;
            }
        }


        /// <summary>
        /// Возвращает список рабочих дней с начала периода без выходных
        /// </summary>
        /// <param name="startPeriod"></param>
        /// <param name="refTime"></param>
        /// <param name="periodType"></param>
        /// <param name="distributionPeriod"></param>
        /// <param name="getWeekends"></param>
        /// <returns></returns>
        public List<DateOnly> GetWorkDatesForPeriod(DateOnly startPeriod, TimeOnly refTime, DistributionPeriodTypeEnum periodType, string distributionPeriod, GetWeekendsDelegate getWeekends)
        {
            var endPeriod = GetEndPeriodDate(startPeriod, periodType, distributionPeriod);

            //не получилось сделать асинхронным
            var weekends = getWeekends.Invoke(startPeriod, endPeriod).ToList(); //.ToListAsync();

            //Берем список дат за период
            var workDays = GetDatesForPeriod(startPeriod, endPeriod);

            //Убираем праздники
            if (weekends.Any())
                workDays = workDays.Where(t => !weekends.Any(x => x == t)).ToList();

            //РАСКОММЕНТИРОВАТЬ НИЖЕ (28 дней), если не использовать календарь выходных дней
            ////Убираем числа старше 28, чтобы сравнять продолжительность с февралем
            //workDays.RemoveAll(t => t.Day > 28);

            //Так как в базе всё храним в UTC то время с 21 часа уже соответствует следующими суткам в календаре Москвы
            //просто сдвигаем на день весь список
            //if (refTime.Hour > 21)
            if (DateResolver.IsCurrentDayRelativeMskTime(refTime.Hour) == false)
                return workDays.Select(wd => wd.AddDays(-1)).ToList();

            //иначе просто отдаём как есть
            return workDays;
        }




        /// <summary>
        /// Возвращает список дней между датами
        /// </summary>
        /// <param name="startPeriod"></param>
        /// <param name="endPeriod"></param>
        /// <returns></returns>
        public List<DateOnly> GetDatesForPeriod(DateOnly startPeriod, DateOnly endPeriod)
        {
            var workDaysList = new List<DateOnly>();

            var currentDay = startPeriod;

            while (currentDay <= endPeriod)
            {
                workDaysList.Add(currentDay);
                currentDay = currentDay.AddDays(1);
            }

            return workDaysList.OrderBy(t => t).ToList();
        }

        /// <summary>
        /// Возвращает список дней между датами
        /// </summary>
        /// <param name="startPeriod"></param>
        /// <param name="endPeriod"></param>
        /// <returns></returns>
        public List<DateOnly> GetDatesForPeriod(DateTimeOffset startPeriod, DateTimeOffset endPeriod)
        {
            return GetDatesForPeriod(DateOnly.FromDateTime(startPeriod.DateTime), DateOnly.FromDateTime(endPeriod.DateTime));
        }


        public DateTimeOffset GetDateWithTimeZoneOffset(DateTimeOffset date, int timeZoneOffsetHours)
        {
            return date.AddHours(timeZoneOffsetHours);
        }

    }
}
