using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
using AsbCloudDb.Model;
using AsbCloudDb.Model.Subsystems;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Background.PeriodicWorks;

public abstract class WorkSubsystemOperationTimeCalcAbstract : Work
{
    protected const int idSubsystemTorqueMaster = 65537;
    protected const int idSubsystemSpinMaster = 65536;
    protected const int idSubsystemAPDRotor = 11;
    protected const int idSubsystemAPDSlide = 12;
    protected const int idSubsystemMse = 2;

    private static TimeSpan obsoleteTime = TimeSpan.FromDays(365 * 100);

    public WorkSubsystemOperationTimeCalcAbstract(string workId)
        : base(workId)
    {
        Timeout = TimeSpan.FromMinutes(30);
    }

    protected override async Task Action(string id, IServiceProvider services, Action<string, double?> onProgressCallback, CancellationToken token)
    {
        var db = services.GetRequiredService<IAsbCloudDbContext>();
        db.Database.SetCommandTimeout(TimeSpan.FromMinutes(5));

        var telemetryLastDetectedDates = await GetTelemetryLastDetectedDates(services, db, token);
        
        var count = telemetryLastDetectedDates.Count();
        var i = 0d;

        foreach (var item in telemetryLastDetectedDates)
        {
            onProgressCallback($"Start handling telemetry: {item.IdTelemetry} from {item.DateDetectedLast}", i++ / count);
            var newOperationsSaub = await OperationTimeAsync(item.IdTelemetry, item.DateDetectedLast, db, token);
            if (newOperationsSaub.Any())
            {
                db.SubsystemOperationTimes.AddRange(newOperationsSaub);
                await db.SaveChangesAsync(token);
            }
        }

        obsoleteTime = TimeSpan.FromDays(3);
    }

    protected abstract Task<IEnumerable<SubsystemOperationTime>> OperationTimeAsync(int idTelemetry, DateTimeOffset geDate, IAsbCloudDbContext db, CancellationToken token);

    private static async Task<IEnumerable<TelemetryDateLast>> GetTelemetryLastDetectedDates(IServiceProvider services, IAsbCloudDbContext db, CancellationToken token)
    {
        var telemetryDataCache = services.GetRequiredService<ITelemetryDataCache<TelemetryDataSaubDto>>();

        var updatingTelemetries = telemetryDataCache.GetStat()
            .Where(tstat => (DateTimeOffset.Now - tstat.DateLast) < obsoleteTime);        

        var telemetryIds = updatingTelemetries
            .Select(t => t.IdTelemetry)
            .ToArray();

        IEnumerable<TelemetryDateLast> lastDetectedDates = await GetLastSubsystemOperationTimeAsync(db, token);
        lastDetectedDates = lastDetectedDates
            .Where(s => telemetryIds.Contains(s.IdTelemetry));

        var result = updatingTelemetries.Select(tstat => new TelemetryDateLast
        {
            IdTelemetry = tstat.IdTelemetry,
            DateDetectedLast = lastDetectedDates.FirstOrDefault(ldd => ldd.IdTelemetry == tstat.IdTelemetry)?.DateDetectedLast
                ?? DateTimeOffset.UnixEpoch,
            DateTelemetryLast = tstat.DateLast
        });
        return result;
    }

    private static async Task<IEnumerable<TelemetryDateLast>> GetLastSubsystemOperationTimeAsync(IAsbCloudDbContext db, CancellationToken token)
    {
        var result = await db.SubsystemOperationTimes
            .GroupBy(o => o.IdTelemetry)
            .Select(g => new TelemetryDateLast
            {
                IdTelemetry = g.Key,
                DateDetectedLast = g.Max(o => o.DateEnd)
            })
            .ToArrayAsync(token);
        return result;
    }

    protected class TelemetryDateLast
    {
        public int IdTelemetry { get; set; }
        public DateTimeOffset DateDetectedLast { get; set; }
        public DateTimeOffset DateTelemetryLast { get; internal set; }
    }
}