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 AsbCloudApp.Requests;
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, DateTimeOffset workTime, CancellationToken token)
    {
        var entities = await BuildQuery(idWell, workTime)
            .AsNoTracking()
            .ToArrayAsync(token);

        return entities.Select(Convert);
    }

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

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

    public async Task<IEnumerable<ScheduleDto>> GetPageAsync(GetStatRequest request, CancellationToken token)
    {
        var idWell = request.IdsWells;
        var idsDrillers = request.IdsDrillers;
        var query =  GetQuery().Where(s =>  request.IdsWells.Contains(s.IdWell));
        if (idsDrillers.Any())
        {
            query = query.Where(s => idsDrillers.Contains(s.IdDriller));
        }

        var result = await query.ToArrayAsync(token);
        return result.Select(Convert);

    }

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

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

    protected override Schedule Convert(ScheduleDto dto)
    {
        var entity = base.Convert(dto);
        entity.DrillStart = dto.DrillStart.ToUniversalTime();
        entity.DrillEnd = dto.DrillEnd.ToUniversalTime();
        return entity;
    }

    protected override ScheduleDto Convert(Schedule entity)
    {
        var hoursOffset = wellService.GetTimezone(entity.IdWell).Hours;
        var timeSpan = TimeSpan.FromHours(hoursOffset);

        var dto = base.Convert(entity);
        dto.DrillStart = entity.DrillStart.ToOffset(timeSpan);
        dto.DrillEnd = entity.DrillEnd.ToOffset(timeSpan);
        return dto;
    }
}