using Insight.Agent.Interfaces;
using Insight.Agent.Messages;
using Insight.Domain.Constants;
using Insight.Infrastructure;
using Insight.Server.Extensions;
using Insight.Server.Network;
using Insight.Server.Network.Handlers.Agent;
using Insight.Server.Network.Handlers.Web;
using Insight.Server.Services;
using Insight.Web.Interfaces;
using Insight.Web.Messages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Net;
using Vaitr.MemoryBus.Hosting;
using Vaitr.Network;

namespace Insight.Server
{
    internal class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = Host.CreateDefaultBuilder(args);
            builder.UseWindowsService();
            builder.UseSystemd();

            builder.ConfigureAppConfiguration(options =>
            {
                options.Defaults();
            });

            builder.ConfigureLogging(options =>
            {
                options.ClearProviders();
                options.SetMinimumLevel(LogLevel.Trace);

                options.AddSimpleConsole(options =>
                {
                    options.IncludeScopes = true;
                    options.SingleLine = true;
                    options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
                });

                options.AddFile($"{Configuration.AppDirectory?.FullName}/" + "logs/server_{Date}.log", LogLevel.Trace, fileSizeLimitBytes: 104857600, retainedFileCountLimit: 10, outputTemplate: "{Timestamp:o} [{Level:u3}] {Message} {NewLine}{Exception}");
            });

            builder.ConfigureServices((host, services) =>
            {
                //var databaseLoggerFactory = LoggerFactory.Create(b =>
                //{
                //    b.AddSimpleConsole();
                //    b.SetMinimumLevel(LogLevel.Debug);
                //});

                // INFRASTRUCTURE
                services.AddDatabase(host.Configuration);

                // AGENT SERVER
                services.UseHostedTcpServer<AgentSession, MemPackSerializer, IAgentMessage>(options =>
                {
                    options.Address = IPAddress.Any;
                    options.Port = host.Configuration.GetValue<int?>(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)");
                    options.KeepAlive = 10000;
                    options.Timeout = 30000;
                    options.Backlog = 128;
                    options.Buffer = 1024 * 1024;
                    options.Parallelism = 0;
                    options.Certificate = host.Configuration.GetValue<string?>(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)");
                    options.CertificatePassword = host.Configuration.GetValue<string?>(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)");
                    options.Compression = Compression.None;
                    options.Encryption = Encryption.Tls12;
                    options.SslPolicy = SslPolicy.None;

                    //options.InputRateLimit = 1024 * 1024;
                    //options.OutputRateLimit = 1024 * 1024;
                });

                services.AddSingleton<AgentHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, AgentHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, DriveHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, Network.Handlers.Agent.EventHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, InterfaceHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, MainboardHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, MemoryHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, OperationSystemHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, PrinterHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, ProcessorHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, ServiceHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, SessionHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, SoftwareHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, StoragePoolHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, SystemInfoHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, TrapHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, UpdateHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, UserHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, VideocardHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, VirtualMaschineHandler>();
                services.AddSingleton<IAgentMessageHandler<AgentSession>, ConsoleHandler>();

                // WEB (FRONTEND-PROXY) SERVER
                services.UseHostedTcpServer<WebSession, MemPackSerializer, IWebMessage>(options =>
                {
                    options.Address = IPAddress.Any;
                    options.Port = host.Configuration.GetValue<int?>(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)");
                    options.KeepAlive = 10000;
                    options.Timeout = 30000;
                    options.Backlog = 128;
                    options.Buffer = 1024 * 1024;
                    options.Parallelism = 0;
                    options.Certificate = host.Configuration.GetValue<string?>(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)");
                    options.CertificatePassword = host.Configuration.GetValue<string?>(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)");
                    options.Compression = Compression.None;
                    options.Encryption = Encryption.Tls12;
                    options.SslPolicy = SslPolicy.None;

                    //options.InputRateLimit = 1024 * 1024;
                    //options.OutputRateLimit = 1024 * 1024;
                });

                services.AddSingleton<IWebMessageHandler<WebSession>, ConsoleProxyHandler>();

                // DISPATCH
                services.AddHostedService<JobService>();
                services.AddHostedService<DispatchService>();

                // GLOBAL DEPENDENCIES
                services.AddMemoryBus();
                services.AddTransient(provider => new HttpClient(new HttpClientHandler
                {
                    ClientCertificateOptions = ClientCertificateOption.Manual,
                    ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true
                }));
            });

            var host = builder.Build();
            await host.RunAsync().ConfigureAwait(false);
        }
    }
}