using Brizco.Domain.Entities.Tasks;
using Task = Brizco.Domain.Entities.Tasks.Task;

namespace Brizco.Core.EntityServices;

public class ActivityService(IMediator mediator, IRepositoryWrapper repositoryWrapper) : IActivityService
{


    public async Task<bool> DoneActivityAsync(Guid activityId, CancellationToken cancellationToken)
    {
        var activity = await repositoryWrapper.SetRepository<Activity>()
            .TableNoTracking
            .FirstOrDefaultAsync(a => a.Id == activityId, cancellationToken);
        if (activity == null)
            throw new AppException("Activity not found", ApiResultStatusCode.NotFound);
        activity.DoneActivity();
        repositoryWrapper.SetRepository<Activity>()
            .Update(activity);
        await repositoryWrapper.SaveChangesAsync(cancellationToken);
        return true;
    }

    public async Task<bool> CompleteActivitiesAsync(List<CompleteActivityRequestDto> requestDtos, CancellationToken cancellationToken)
    {
        foreach (var activityRequestDto in requestDtos)
        {
            var activity = await repositoryWrapper.SetRepository<Activity>().TableNoTracking
                .FirstOrDefaultAsync(a => a.Id == activityRequestDto.ActivityId, cancellationToken);
            if (activity == null)
                continue;

            activity.CompleteActivity(activityRequestDto.IsCompleted, activityRequestDto.PerformanceDescription);
            repositoryWrapper.SetRepository<Activity>().Update(activity);
            await repositoryWrapper.SaveChangesAsync(cancellationToken);
        }

        return true;
    }


    public async Task<bool> UnDoneActivityAsync(Guid activityId, string undoneReason, CancellationToken cancellationToken)
    {
        var activity = await repositoryWrapper.SetRepository<Activity>()
            .TableNoTracking
            .FirstOrDefaultAsync(a => a.Id == activityId, cancellationToken);
        if (activity == null)
            throw new AppException("Activity not found ", ApiResultStatusCode.NotFound);
        activity.UnDoneActivity(undoneReason);
        repositoryWrapper.SetRepository<Activity>()
            .Update(activity);
        await repositoryWrapper.SaveChangesAsync(cancellationToken);
        return true;
    }

    public async Task<bool> UpdateActivitiesByShiftPlan(Guid shiftPlanId, CancellationToken cancellationToken)
    {
        var shiftPlan = await mediator.Send(new GetShiftPlanQuery(shiftPlanId), cancellationToken);
        if (shiftPlan.Id == Guid.Empty)
            return false;

        var dailyTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                    .TaskId
                                where taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId && task.ScheduleType == TaskScheduleType.Daily
                                select task).AsNoTracking().ToListAsync(cancellationToken);

        var weeklyTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                 join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                 join taskDay in repositoryWrapper.SetRepository<TaskDay>().Entities on task.Id equals taskDay.TaskId
                                 join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                     .TaskId
                                 where taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId &&
                                       taskDay.DayOfWeek == shiftPlan.PlanFor.DayOfWeek && task.ScheduleType == TaskScheduleType.Weekly
                                 group task by task.Id into groupedTask
                                 select groupedTask.FirstOrDefault()).AsNoTracking().ToListAsync(cancellationToken);

        var customTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                 join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                 join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                     .TaskId
                                 where taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId &&
                                       task.ScheduleType == TaskScheduleType.Custom && task.SetFor.Date == shiftPlan.PlanFor.Date
                                 select task).AsNoTracking().ToListAsync(cancellationToken);

        var tasks = new List<Task>();
        tasks.AddRange(weeklyTasks);

        tasks.AddRange(dailyTasks);
        tasks.AddRange(customTasks);

        var shiftPlanPositions = await repositoryWrapper.SetRepository<ShiftPlanUser>()
            .TableNoTracking
            .Where(spu => spu.ShiftPlanId == shiftPlan.Id)
            .ToListAsync(cancellationToken);


        var activities = await repositoryWrapper.SetRepository<Activity>()
            .TableNoTracking
            .Where(a => a.ShiftPlanId == shiftPlan.ShiftId && a.SetFor.Date == shiftPlan.PlanFor.Date)
            .Select(ActivityMapper.ProjectToLDto)
            .ToListAsync(cancellationToken);
        foreach (var activity in activities)
        {
            var foundedTask = tasks.FirstOrDefault(t => t.Title == activity.Title);
            if (foundedTask == null)
                continue;

            var taskPositions = await repositoryWrapper.SetRepository<TaskPosition>()
                .TableNoTracking
                .Where(tp => tp.TaskId == foundedTask.Id)
                .ToListAsync(cancellationToken);

            foreach (var taskPosition in taskPositions)
            {
                var foundedUser = shiftPlanPositions.FirstOrDefault(spu => spu.PositionId == taskPosition.PositionId);
                if (foundedUser == null)
                    continue;
                if (activity.UserId != foundedUser.UserId)
                {

                    await mediator.Send(new UpdateActivityCommand(activity.Id, activity.Status, activity.DoneAt, activity.PerformanceDescription,
                    activity.Type, activity.Title, activity.Description, activity.IsDisposable, activity.SetFor, activity.HasDisposed, activity.Amount
                    , activity.AmountType, activity.ScheduleType, shiftPlan.Id, foundedUser.UserId), cancellationToken);
                }
            }
        }
        return true;
    }

    public async Task<bool> CreateActivitiesByShiftPlan(Guid shiftPlanId, CancellationToken cancellationToken) {
        var shiftPlan = await mediator.Send(new GetShiftPlanQuery(shiftPlanId), cancellationToken);
        if (shiftPlan.Id == Guid.Empty)
            return false;

        var dailyTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                    .TaskId
                                where task.IsActivity == false && taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId && task.ScheduleType == TaskScheduleType.Daily
                                select task).AsNoTracking().ToListAsync(cancellationToken);

        var weeklyTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                 join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                 join taskDay in repositoryWrapper.SetRepository<TaskDay>().Entities on task.Id equals taskDay.TaskId
                                 join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                     .TaskId
                                 where task.IsActivity == false && taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId &&
                                       taskDay.DayOfWeek == shiftPlan.PlanFor.DayOfWeek && task.ScheduleType == TaskScheduleType.Weekly
                                 group task by task.Id into groupedTask
                                 select groupedTask.FirstOrDefault()).AsNoTracking().ToListAsync(cancellationToken);

        var customTasks = await (from task in repositoryWrapper.SetRepository<Task>().Entities
                                 join taskShift in repositoryWrapper.SetRepository<TaskShift>().Entities on task.Id equals taskShift.TaskId
                                 join taskRoutine in repositoryWrapper.SetRepository<TaskRoutine>().Entities on task.Id equals taskRoutine
                                     .TaskId
                                 where task.IsActivity == false && taskShift.ShiftId == shiftPlan.ShiftId && taskRoutine.RoutineId == shiftPlan.RoutineId &&
                                       task.ScheduleType == TaskScheduleType.Custom && task.SetFor.Date == shiftPlan.PlanFor.Date
                                 select task).AsNoTracking().ToListAsync(cancellationToken);

        var tasks = new List<Task>();
        tasks.AddRange(weeklyTasks);
        tasks.AddRange(dailyTasks);
        tasks.AddRange(customTasks);


        var shiftPlanPositions = await repositoryWrapper.SetRepository<ShiftPlanUser>()
            .TableNoTracking
            .Where(spu => spu.ShiftPlanId == shiftPlan.Id)
            .ToListAsync(cancellationToken);

        foreach (var task in tasks)
        {
            var taskPositions = await repositoryWrapper.SetRepository<TaskPosition>()
                .TableNoTracking
                .Where(tp => tp.TaskId == task.Id)
                .ToListAsync(cancellationToken);

            foreach (var taskPosition in taskPositions)
            {
                var fundedUser = shiftPlanPositions.FirstOrDefault(spu => spu.PositionId == taskPosition.PositionId);
                if (fundedUser != null)
                {
                    await mediator.Send(new CreateActivityCommand(task.Type, task.Title, task.Description, task.IsDisposable,
                        shiftPlan.PlanFor, task.HasDisposed, task.Amount, task.AmountType,
                        task.ScheduleType, shiftPlan.Id, fundedUser.UserId), cancellationToken);
                }
            }
        }

        return true;
    }
}