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

namespace DD.Persistence.Repository.Repositories;
public class TimeSeriesDataRepository<TEntity, TDto> : ITimeSeriesDataRepository<TDto>
    where TEntity : class, ITimestampedData, new()
    where TDto : class, ITimeSeriesAbstractDto, new()
{
    private readonly DbContext db;

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

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

    public virtual async Task<DatesRangeDto?> GetDatesRange(CancellationToken token)
    {
        var query = GetQueryReadOnly();
        var minDate = await query.MinAsync(o => o.Date, token);
        var maxDate = await query.MaxAsync(o => o.Date, token);

        return new DatesRangeDto
        {
            From = minDate,
            To = maxDate
        };
    }

    public virtual async Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset date, CancellationToken token)
    {
        var query = this.db.Set<TEntity>().Where(e => e.Date > date);
        var entities = await query.ToArrayAsync(token);

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

        return dtos;
    }

    public virtual async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
    {
        var entities = dtos.Select(d => d.Adapt<TEntity>());

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

        return result;
    }

    protected async Task<IEnumerable<TDto>> GetLastAsync(int takeCount, CancellationToken token)
    {
        var query = GetQueryReadOnly()
            .OrderByDescending(e => e.Date)
            .Take(takeCount);

        var entities = await query.ToArrayAsync(token);
        var dtos = entities.Select(e => e.Adapt<TDto>());

        return dtos;
    }

    protected async Task<TDto?> GetFirstAsync(CancellationToken token)
    {
        var query = GetQueryReadOnly()
            .OrderBy(e => e.Date);

        var entity = await query.FirstOrDefaultAsync(token);

        if (entity == null)
            return null;

        var dto = entity.Adapt<TDto>();
        return dto;
    }

    public async virtual Task<IEnumerable<TDto>> GetResampledData(
        DateTimeOffset dateBegin,
        double intervalSec = 600d,
        int approxPointsCount = 1024,
        CancellationToken token = default)
    {
        var dtos = await GetGtDate(dateBegin, token);

        var dateEnd = dateBegin.AddSeconds(intervalSec);
        dtos = dtos
            .Where(i => i.Date <= dateEnd);

        var ratio = dtos.Count() / approxPointsCount;
        if (ratio > 1)
            dtos = dtos
                .Where((_, index) => index % ratio == 0);

        return dtos;
    }
}