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

namespace DD.Persistence.Repository.Repositories;

public class TimeSeriesDataCachedRepository<TEntity, TDto> : TimeSeriesDataRepository<TEntity, TDto>
    where TEntity : class, ITimestampedData, new()
    where TDto : class, ITimeSeriesAbstractDto, new()
{
    public static TDto? FirstByDate { get; private set; }
    public static CyclicArray<TDto> LastData { get; } = new CyclicArray<TDto>(CacheItemsCount);

    private const int CacheItemsCount = 3600;

    public TimeSeriesDataCachedRepository(DbContext db) : base(db)
    {
        Task.Run(async () =>
        {
            var firstDateItem = await base.GetFirstAsync(CancellationToken.None);
            if (firstDateItem == null)
            {
                return;
            }

            FirstByDate = firstDateItem;

            var dtos = await base.GetLastAsync(CacheItemsCount, CancellationToken.None);
            dtos = dtos.OrderBy(d => d.Date);
            LastData.AddRange(dtos);
        }).Wait();
    }

    public override async Task<IEnumerable<TDto>> GetGtDate(DateTimeOffset dateBegin, CancellationToken token)
    {

        if (LastData.Count == 0 || LastData[0].Date > dateBegin)
        {
            var dtos = await base.GetGtDate(dateBegin, token);
            return dtos;
        }

        var items = LastData
            .Where(i => i.Date >= dateBegin);

        return items;
    }

    public override async Task<int> AddRange(IEnumerable<TDto> dtos, CancellationToken token)
    {
        var result = await base.AddRange(dtos, token);
        if (result > 0)
        {

            dtos = dtos.OrderBy(x => x.Date);

            FirstByDate = dtos.First();
            LastData.AddRange(dtos);
        }

        return result;
    }

    public override async Task<DatesRangeDto?> GetDatesRange(CancellationToken token)
    {
        if (FirstByDate == null)
            return null;

        return await Task.Run(() =>
        {
            return new DatesRangeDto
            {
                From = FirstByDate.Date,
                To = LastData[^1].Date
            };
        });
    }

    public override async Task<IEnumerable<TDto>> GetResampledData(
        DateTimeOffset dateBegin,
        double intervalSec = 600d,
        int approxPointsCount = 1024,
        CancellationToken token = default)
    {
        var dtos = LastData.Where(i => i.Date >= dateBegin);
        if (LastData.Count == 0 || LastData[0].Date > dateBegin)
        {
            dtos = await base.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;
    }
}