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

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) =>
        {
            // AGENT
            services.AddAgentServices(host.Configuration);

            // WEB
            services.AddWebServices(host.Configuration);

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

            // BACKGROUND SERVICES
            services.AddHostedService<JobService>();
            services.AddHostedService<DispatchService>();

            // GLOBAL DEPENDENCIES
            services.AddSingleton<Bus>();
            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);
    }
}

internal static class ServiceExtensions
{
    internal static IServiceCollection AddAgentServices(this IServiceCollection services, IConfiguration configuration)
    {
        // AGENT SERVER
        services.UseHostedServer<AgentSession, IMessage>(options =>
        {
            options.Address = IPAddress.Any;
            options.Port = configuration.GetValue<int?>(Appsettings.AgentServerPort) ?? throw new Exception($"{Appsettings.AgentServerPort} value not set (appsettings)");
            options.Keepalive = 10000;
            options.Timeout = 30000;
            options.Backlog = 128;

            options.Compression = true;
            options.Encryption = Encryption.Tls12;
            options.Certificate = configuration.GetValue<string?>(Appsettings.AgentServerCertificate) ?? throw new Exception($"{Appsettings.AgentServerCertificate} value not set (appsettings)");
            options.CertificatePassword = configuration.GetValue<string?>(Appsettings.AgentServerCertificatePassword) ?? throw new Exception($"{Appsettings.AgentServerCertificatePassword} value not set (appsettings)");

            options.UseSerializer<AgentSession, IMessage, MemPackSerializer<IMessage>>();
        });

        // HANDLER  
        services.AddSingleton<AgentHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, CustomHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, ProxyHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, AgentHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, DriveHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, Network.Agent.Handlers.EventHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, InterfaceHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, MainboardHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, MemoryHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, OperationSystemHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, PrinterHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, ProcessorHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, ServiceHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, SessionHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, SoftwareHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, StoragePoolHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, SystemInfoHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, TrapHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, UpdateHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, UserHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, VideocardHandler>();
        services.AddSingleton<IMessageHandler<AgentSession>, VirtualMaschineHandler>();
        
        return services;
    }

    internal static IServiceCollection AddWebServices(this IServiceCollection services, IConfiguration configuration)
    {
        // SERVER
        services.UseHostedServer<WebSession, IMessage>(options =>
        {
            options.Address = IPAddress.Any;
            options.Port = configuration.GetValue<int?>(Appsettings.WebServerPort) ?? throw new Exception($"{Appsettings.WebServerPort} value not set (appsettings)");
            options.Keepalive = 10000;
            options.Timeout = 30000;
            options.Backlog = 128;
            options.Encryption = Encryption.Tls12;
            options.Compression = true;

            options.Certificate = configuration.GetValue<string?>(Appsettings.WebServerCertificate) ?? throw new Exception($"{Appsettings.WebServerCertificate} value not set (appsettings)");
            options.CertificatePassword = configuration.GetValue<string?>(Appsettings.WebServerCertificatePassword) ?? throw new Exception($"{Appsettings.WebServerCertificatePassword} value not set (appsettings)");

            options.UseSerializer<WebSession, IMessage, MemPackSerializer<IMessage>>();
        });

        // HANDLER  
        services.AddSingleton<IMessageHandler<WebSession>, ProxyHandler>();

        return services;
    }
}