using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Mapster;

namespace AsbCloudInfrastructure.Repository
{

    public class ScheduleRepository : CrudWellRelatedRepositoryBase<ScheduleDto, Schedule>, 
        IScheduleRepository
    {
        private readonly IWellService wellService;

        public ScheduleRepository(IAsbCloudDbContext context, IWellService wellService)
            : base(context, dbSet => dbSet.Include(s => s.Driller))
        {
            this.wellService = wellService;
        }

        public async Task<IEnumerable<ScheduleDto>> GetAsync(int idWell, DateTime workTime, CancellationToken token)
        {
            var entities = await BuildQuery(idWell, workTime)
                .AsNoTracking()
                .ToArrayAsync(token);

            return entities.Select(Convert);
        }

        public async Task<DrillerDto?> GetOrDefaultDrillerAsync(int idWell, DateTime workTime, CancellationToken token)
        {
            var entities = await BuildQuery(idWell, workTime)
                .AsNoTracking()
                .ToArrayAsync(token);

            if (!entities.Any())
                return null;
            
            var hoursOffset = wellService.GetTimezone(idWell).Hours;
            var remoteDate = workTime.ToUtcDateTimeOffset(hoursOffset).ToRemoteDateTime(hoursOffset);
            var time = new TimeOnly(remoteDate.Hour, remoteDate.Minute, remoteDate.Second);
            
            var entity = entities.FirstOrDefault(s =>
                s.ShiftStart > s.ShiftEnd ^
                (time >= s.ShiftStart && time < s.ShiftEnd)
            );
            
            return entity?.Driller.Adapt<DrillerDto>();
        }

        private IQueryable<Schedule> BuildQuery(int idWell, DateTime workTime)
        {
            var hoursOffset = wellService.GetTimezone(idWell).Hours;

            var workTimeDateTime = workTime.ToUtcDateTimeOffset(hoursOffset);
            
            return GetQuery().Where(s => s.IdWell == idWell
                                  && s.DrillStart <= workTimeDateTime
                                  && s.DrillEnd >= workTimeDateTime);
        }

        protected override Schedule Convert(ScheduleDto dto)
        {
            var hoursOffset = wellService.GetTimezone(dto.IdWell).Hours;
            var entity = base.Convert(dto);
            entity.DrillStart = dto.DrillStart.ToUtcDateTimeOffset(hoursOffset);
            entity.DrillEnd = dto.DrillEnd.ToUtcDateTimeOffset(hoursOffset);
            return entity;
        }

        protected override ScheduleDto Convert(Schedule entity)
        {
            var hoursOffset = wellService.GetTimezone(entity.IdWell).Hours;
            var dto = base.Convert(entity);
            dto.DrillStart = entity.DrillStart.ToRemoteDateTime(hoursOffset);
            dto.DrillEnd = entity.DrillEnd.ToRemoteDateTime(hoursOffset);
            return dto;
        }
    }

}