using MD.PersianDateTime.Standard;

namespace Brizco.Repository.Handlers.ShiftPlans;

public class GetShiftPlansQueryHandler(IRepositoryWrapper repositoryWrapper, ICurrentUserService currentUserService) : IRequestHandler<GetShiftPlansQuery, List<ShiftPlanSDto>>
{
    public async Task<List<ShiftPlanSDto>> Handle(GetShiftPlansQuery request, CancellationToken cancellationToken)
    {
        if (currentUserService.ComplexId == null)
            throw new AppException("ComplexId is null", ApiResultStatusCode.NotFound);
        if (!Guid.TryParse(currentUserService.ComplexId, out Guid complexId))
            throw new AppException("ComplexId is wrong", ApiResultStatusCode.NotFound);

        IQueryable<ShiftPlan> baseQuery = repositoryWrapper.SetRepository<ShiftPlan>()
            .TableNoTracking
            .OrderByDescending(s => s.CreatedAt)
            .Where(s => s.ComplexId == complexId);

        List<ShiftPlanSDto> shiftPlans = new List<ShiftPlanSDto>();
        bool setActiviesCount = false;

        if (request.SelectedDate == 0)
        {
            shiftPlans = await baseQuery
                .Skip(request.Page * 15).Take(15)
                .Select(ShiftPlanMapper.ProjectToSDto)
                .ToListAsync(cancellationToken);

        }
        else if (request.SelectedDate > 0)
        {
            var selectedDate = DateTimeExtensions.UnixTimeStampToDateTime(request.SelectedDate);
            shiftPlans = await baseQuery.Where(s => s.PlanFor.Date == selectedDate.Date)
                .Skip(request.Page * 15).Take(15)
                .Select(ShiftPlanMapper.ProjectToSDto)
                .ToListAsync(cancellationToken);
            setActiviesCount = true;
        }

        if (request.DateTimeQueryFilter != null)
        {

            setActiviesCount = true;
            switch (request.DateTimeQueryFilter)
            {
                case DateTimeQueryFilter.Today:
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date == DateTime.Now.Date)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.Yesterday:
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date == DateTime.Now.AddDays(-1).Date)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.PastWeek:
                    DateTime pastWeekToday = DateTime.Today;
                    int pastWeekDelta = DayOfWeek.Saturday - pastWeekToday.DayOfWeek;
                    if (pastWeekDelta > 0) pastWeekDelta -= 7;
                    DateTime startOfPastWeek = DateTime.Today.AddDays(pastWeekDelta).AddDays(-7);
                    DateTime endOfPastWeek = startOfPastWeek.AddDays(6);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfPastWeek.Date && s.PlanFor.Date <= endOfPastWeek.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.NextWeek:
                    DateTime startOfNextWeek = DateTime.Today.AddDays(7 - (int)DateTime.Today.DayOfWeek + 1);
                    DateTime endOfNextWeek = startOfNextWeek.AddDays(6);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfNextWeek.Date && s.PlanFor.Date <= endOfNextWeek.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.ThisWeek:
                    DateTime today = DateTime.Today;
                    int delta = DayOfWeek.Saturday - today.DayOfWeek;
                    if (delta > 0) delta -= 7;
                    DateTime startOfWeek = today.AddDays(delta);
                    DateTime endOfWeek = startOfWeek.AddDays(6);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfWeek.Date && s.PlanFor.Date <= endOfWeek.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.PastMonth:
                    
                    DateTime startOfPastMonth = new PersianDateTime(PersianDateTime.Today.AddMonths(-1).Year, PersianDateTime.Today.AddMonths(-1).Month, 1).ToDateTime();
                    DateTime endOfPastMonth = startOfPastMonth.AddMonths(1);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfPastMonth.Date && s.PlanFor.Date < endOfPastMonth.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case DateTimeQueryFilter.NextMonth:
                    DateTime startOfNextMonth = new PersianDateTime(PersianDateTime.Today.AddMonths(1).Year, PersianDateTime.Today.AddMonths(1).Month, 1).ToDateTime();
                    DateTime endOfNextMonth = startOfNextMonth.AddMonths(1);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfNextMonth.Date && s.PlanFor.Date < endOfNextMonth.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;

                case DateTimeQueryFilter.ThisMonth:
                    DateTime startOfThisMonth = new PersianDateTime(PersianDateTime.Today.Year, PersianDateTime.Today.Month, 1).ToDateTime();
                    DateTime endOfThisMonth = startOfThisMonth.AddMonths(1);
                    shiftPlans = await baseQuery.Where(s => s.PlanFor.Date >= startOfThisMonth.Date && s.PlanFor.Date < endOfThisMonth.Date)
                        .OrderByDescending(s => s.CreatedAt)
                        .Skip(request.Page * 15).Take(15)
                        .Select(ShiftPlanMapper.ProjectToSDto)
                        .ToListAsync(cancellationToken);
                    break;
                case null:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();

            }
        }

        if (setActiviesCount)
        {

            foreach (var shiftPlan in shiftPlans)
            {
                var activitiesCount = await repositoryWrapper.SetRepository<Domain.Entities.Tasks.Activity>()
                    .TableNoTracking
                    .CountAsync(a => a.SetFor.Date == shiftPlan.PlanFor.Date && a.ShiftPlanId == shiftPlan.Id, cancellationToken);
                var doneActivitiesCount = await repositoryWrapper.SetRepository<Domain.Entities.Tasks.Activity>()
                    .TableNoTracking
                    .CountAsync(a => a.Status == ActivityStatus.Done && a.SetFor.Date == shiftPlan.PlanFor.Date && a.ShiftPlanId == shiftPlan.Id, cancellationToken);
                var undoneActivitiesCount = await repositoryWrapper.SetRepository<Domain.Entities.Tasks.Activity>()
                    .TableNoTracking
                    .CountAsync(a => a.Status == ActivityStatus.UnDone && a.SetFor.Date == shiftPlan.PlanFor.Date && a.ShiftPlanId == shiftPlan.Id, cancellationToken);
                shiftPlan.UndoneActivitiesCount = undoneActivitiesCount;
                shiftPlan.DoneActivitiesCount = doneActivitiesCount;
                shiftPlan.TotalActivitiesCount = activitiesCount;
            }
        }

        foreach (var shiftPlan in shiftPlans)
        {
            shiftPlan.StaffCount = await repositoryWrapper.SetRepository<ShiftPlanUser>()
                .TableNoTracking
                .CountAsync(spu => spu.ShiftPlanId == shiftPlan.Id, cancellationToken);

        }

        return shiftPlans;
    }
}