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 SetpointRepository : ISetpointRepository
    {
        private readonly DbContext db;
        public SetpointRepository(DbContext db)
        {
            this.db = db;
        }

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

        public async Task<IEnumerable<SetpointValueDto>> GetCurrent(IEnumerable<Guid> setpointKeys, CancellationToken token)
        {
            var query = GetQueryReadOnly();
            var entities = await query
                .Where(e => setpointKeys.Contains(e.Key))
                .ToArrayAsync(token);
            var dtos = entities.Select(e => e.Adapt<SetpointValueDto>());

            return dtos;
        }

        public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
        {
            var query = GetQueryReadOnly();
            var entities = await query
                .Where(e => setpointKeys.Contains(e.Key))
                .ToArrayAsync(token);
            var filteredEntities = entities
                .GroupBy(e => e.Key)
                .Select(e => e.OrderBy(o => o.Created))
                .Select(e => e.Where(e => e.Created <= historyMoment).Last());
            var dtos = filteredEntities
                .Select(e => e.Adapt<SetpointValueDto>());

            return dtos;
        }

        public async Task<IEnumerable<SetpointLogDto>> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token)
        {
            var query = GetQueryReadOnly();
            var entities = await query
                .Where(e => e.Created >= dateBegin)
                .Take(take)
                .ToArrayAsync(token);
            var dtos = entities
                .Select(e => e.Adapt<SetpointLogDto>());

            return dtos;
        }

        public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
        {
            var query = GetQueryReadOnly()
                .GroupBy(e => 1)
                .Select(group => new
                {
                    Min = group.Min(e => e.Created),
                    Max = group.Max(e => e.Created),
                });
            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<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
        {
            var query = GetQueryReadOnly();
            var entities = await query
                .Where(e => setpointKeys.Contains(e.Key))
                .ToArrayAsync(token);
            var dtos = entities
                .GroupBy(e => e.Key)
                .ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));

            return dtos;
        }

        public async Task Add(Guid setpointKey, object newValue, Guid idUser, CancellationToken token)
        {
            var entity = new Setpoint()
            {
                Key = setpointKey,
                Value = newValue,
                IdUser = idUser,
                Created = DateTimeOffset.UtcNow
            };

            await db.Set<Setpoint>().AddAsync(entity, token);
            await db.SaveChangesAsync(token);
        }
    }
}