using Mapster;
using Microsoft.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Models;
using DD.Persistence.Repositories;

namespace DD.Persistence.Repository.Repositories;
public class ParameterRepository : IParameterRepository
{
    private DbContext db;

    public ParameterRepository(DbContext db)
    {
        this.db = db;
    }

    protected virtual IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();

    public async Task<DatesRangeDto> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token)
    {
        var query = GetQueryReadOnly()
            .Where(e => e.DiscriminatorId == idDiscriminator)
            .GroupBy(e => 1)
            .Select(group => new
            {
                Min = group.Min(e => e.Timestamp),
                Max = group.Max(e => e.Timestamp),
            });
        var values = await query.FirstOrDefaultAsync(token);
        var result = new DatesRangeDto()
        {
            From = values?.Min ?? DateTimeOffset.MinValue,
            To = values?.Max ?? DateTimeOffset.MaxValue
        };

        return result;
    }

    public async Task<IEnumerable<ParameterDto>> GetPart(Guid idDiscriminator, DateTimeOffset dateBegin, int take, CancellationToken token)
    {
        var query = GetQueryReadOnly();
        var universalDate = dateBegin.ToUniversalTime();
        var entities = await query
            .Where(e => e.DiscriminatorId == idDiscriminator && e.Timestamp >= universalDate)
            .Take(take)
            .ToArrayAsync(token);
        var dtos = entities.Select(e => e.Adapt<ParameterDto>());

        return dtos;
    }

    public async Task<IEnumerable<ParameterDto>> GetValuesForGraph(Guid discriminatorId, DateTimeOffset dateFrom, DateTimeOffset dateTo,
        int approxPointsCount, int? ratio, CancellationToken token)
    {
        var query = db.Set<ParameterData>().AsNoTracking();
        var universalDateFrom = dateFrom.ToUniversalTime();
        var universalDateTo = dateTo.ToUniversalTime();

        query = query
            .Where(e => e.DiscriminatorId == discriminatorId)
            .Where(e => e.Timestamp >= universalDateFrom && e.Timestamp <= universalDateTo)
            .OrderBy(e => e.Timestamp);
        if (ratio != null)
        {
            query = query.Where(e => ((int)(e.Timestamp - dateFrom).TotalSeconds) % ratio == 0);
        }

        var entities = await query
            .Take((int)(2.5 * approxPointsCount))
            .ToArrayAsync(token);

        var dtos = entities.Select(e => e.Adapt<ParameterDto>());

        return dtos;
    }

    public async Task<int> AddRange(IEnumerable<ParameterDto> dtos, CancellationToken token)
    {
        var entities = dtos.Select(e => e.Adapt<ParameterData>());

        await db.Set<ParameterData>().AddRangeAsync(entities, token);
        var result = await db.SaveChangesAsync(token);

        return result;
    }
}