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

namespace AsbCloudInfrastructure.Repository;

public class DetectedOperationRepository 
    : CrudRepositoryBase<DetectedOperationDto, DetectedOperation>, IDetectedOperationRepository
{
    private readonly ITelemetryService telemetryService;

    public DetectedOperationRepository(IAsbCloudDbContext context,
        ITelemetryService telemetryService)
        : base(context)
    {
        this.telemetryService = telemetryService;
    }

    public async Task<IDictionary<int, DetectedOperationDto>> GetLastDetectedOperationsAsync(CancellationToken token)
    {
        var entities = await dbContext.Set<DetectedOperation>()
            .GroupBy(o => o.IdTelemetry)
            .Select(g => new
            {
                IdTelemetry = g.Key,
                LastDetectedOperation = g.OrderBy(o => o.DateEnd).Last()
            })
            .ToArrayAsync(token);

        var dtos = entities.ToDictionary(x => x.IdTelemetry,
            x => Convert(x.LastDetectedOperation));

        return dtos;
    }

    public async Task<int> DeleteAsync(DetectedOperationByTelemetryRequest request, CancellationToken token)
    {
        var query = BuildQuery(request);
        dbContext.Set<DetectedOperation>().RemoveRange(query);
        return await dbContext.SaveChangesAsync(token);
    }

    public async Task<int> DeleteAsync(TelemetryPartDeleteRequest request, CancellationToken token)
    {
        var query = BuildQuery(request);
        dbContext.Set<DetectedOperation>().RemoveRange(query);
        return await dbContext.SaveChangesAsync(token);
    }

    public async Task<PaginationContainer<DetectedOperationDto>> GetPageAsync(DetectedOperationByTelemetryRequest request, CancellationToken token)
    {
        var skip = request.Skip ?? 0;
        var take = request.Take ?? 32;

        var query = BuildQuery(request);

        var entities = await query.Skip(skip)
            .Take(take)
            .AsNoTracking()
            .ToArrayAsync(token);

        var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
        
        var paginationContainer = new PaginationContainer<DetectedOperationDto>
        {
            Skip = skip,
            Take = take,
            Count = await query.CountAsync(token),
            Items = entities.Select(o => Convert(o, offset))
        };

        return paginationContainer;
    }
    
    public async Task<IEnumerable<DetectedOperationDto>> Get(DetectedOperationByTelemetryRequest request, CancellationToken token)
    {
        var query = BuildQuery(request)
            .Include(o => o.OperationCategory);
        var entities = await query.AsNoTracking().ToArrayAsync(token);
        var offset = telemetryService.GetTimezone(request.IdTelemetry).Offset;
        var dtos = entities.Select(o => Convert(o, offset));

        return dtos;
    }
    
    private IQueryable<DetectedOperation> BuildQuery(DetectedOperationByTelemetryRequest request)
    {
        var query = dbContext.Set<DetectedOperation>()
            .OrderBy(o => o.DateStart)
            .ThenBy(o => o.DepthStart)
            .Where(o => o.IdTelemetry == request.IdTelemetry);

        if (request.IdsCategories.Any())
            query = query.Where(o => request.IdsCategories.Contains(o.IdCategory));

        if (request.GeDepthStart is not null)
            query = query.Where(o => o.DepthStart >= request.GeDepthStart);

        if (request.LeDepthEnd is not null)
            query = query.Where(o => o.DepthEnd <= request.LeDepthEnd);

        if (request.GeDateStart is not null)
        {
            var geDate = request.GeDateStart.Value.ToUniversalTime();
            query = query.Where(o => o.DateStart >= geDate);
        }
        
        if (request.LeDateEnd is not null)
        {
            var leDate = request.LeDateEnd.Value.ToUniversalTime();
            query = query.Where(o => o.DateEnd <= leDate);
        }

        if (request.SortFields?.Any() == true)
            query = query.SortBy(request.SortFields);

        return query;
    }

    private IQueryable<DetectedOperation> BuildQuery(TelemetryPartDeleteRequest request)
    {
        var query = dbContext.Set<DetectedOperation>()
            .Where(o => o.IdTelemetry == request.IdTelemetry);

        if (request.LeDate is not null)
        {
            var leDate = request.LeDate.Value.ToUniversalTime();
            query = query.Where(o => o.DateStart <= leDate);
        }

        if (request.GeDate is not null)
        {
            var geDate = request.GeDate.Value.ToUniversalTime();
            query = query.Where(o => o.DateEnd >= geDate);
        }

        return query;
    }

    private static DetectedOperationDto Convert(DetectedOperation src, TimeSpan offset)
    {
        var dto = src.Adapt<DetectedOperationDto>();
        dto.DateStart = src.DateStart.ToOffset(offset);
        dto.DateEnd = src.DateEnd.ToOffset(offset);
        dto.EnabledSubsystems = src.EnabledSubsystems;
        return dto;
    }

    protected override DetectedOperation Convert(DetectedOperationDto src)
    {
        var entity = src.Adapt<DetectedOperation>();
        entity.DateStart = src.DateStart.ToUniversalTime();
        entity.DateEnd = src.DateEnd.ToUniversalTime();
        return entity;
    }
}