using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.DetectOperations;
using AsbCloudInfrastructure.Services.Subsystems;
using AsbCloudInfrastructure.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading.Tasks;
using System.Threading;
using AsbCloudInfrastructure.Background;
using AsbCloudApp.Data.SAUB;
using AsbCloudInfrastructure.Services.SAUB;

namespace AsbCloudInfrastructure
{

    public class Startup
    {
        public static void BeforeRunHandler(IHost host)
        {
            using var scope = host.Services.CreateScope();
            var provider = scope.ServiceProvider;

            var context = provider.GetRequiredService<IAsbCloudDbContext>();
            context.Database.SetCommandTimeout(TimeSpan.FromSeconds(2 * 60));
            context.Database.Migrate();

            // TODO: Сделать инициализацию кеша телеметрии более явной.
            _ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSaubDto>>();
            _ = provider.GetRequiredService<TelemetryDataCache<TelemetryDataSpinDto>>();

            var backgroundWorker = provider.GetRequiredService<BackgroundWorker>();
            backgroundWorker.Push(WellInfoService.MakeWork());
            backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork());
            backgroundWorker.Push(SubsystemOperationTimeCalcWorkFactory.MakeWork());
            backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork());
            backgroundWorker.Push(MakeMemoryMonitoringWork());

            var notificationBackgroundWorker = provider.GetRequiredService<NotificationBackgroundWorker>();
            
            Task.Delay(1_000)
                .ContinueWith(async (_) =>
                {
                    await backgroundWorker.StartAsync(CancellationToken.None);
                    await notificationBackgroundWorker.StartAsync(CancellationToken.None);
                });
        }

        static WorkPeriodic MakeMemoryMonitoringWork()
        {
            var workId = "Memory monitoring";
            var workAction = (string _, IServiceProvider _, CancellationToken _) => {
                var bytes = GC.GetTotalMemory(false);
                var bytesString = FromatBytes(bytes);
                System.Diagnostics.Trace.TraceInformation($"Total memory allocated is {bytesString} bytes. DbContext count is:{AsbCloudDbContext.ReferenceCount}");
                return Task.CompletedTask;
            };
            var workPeriod = TimeSpan.FromMinutes(1);
            var work = new WorkPeriodic(workId, workAction, workPeriod);
            return work;
        }

        static string FromatBytes(long bytes)
        {
            const double gigaByte = 1024 * 1024 * 1024;
            const double megaByte = 1024 * 1024;
            const double kiloByte = 1024;
            
            if (bytes > 10 * gigaByte)
                return (bytes / gigaByte).ToString("### ### ###.## Gb");

            if (bytes > 10 * megaByte)
                return (bytes / megaByte).ToString("### ### ###.## Mb");

            if (bytes > 10 * kiloByte)
                return (bytes / megaByte).ToString("### ### ###.## Kb");

            return bytes.ToString("### ### ###");
        }
    }

}