using Insight.Domain.Constants;
using Insight.Domain.Interfaces;
using Insight.Domain.Network;
using Insight.Infrastructure;
using Insight.Web.Hosting;
using Insight.Web.Middleware;
using Insight.Web.Network.Broker;
using Insight.Web.Network.Broker.Handlers;
using Insight.Web.Network.Remote;
using Insight.Web.Network.Remote.Handlers;
using System.Net;
using Vaitr.Bus;
using Vaitr.Network;
using Vaitr.Network.Hosting;

namespace Insight.Web;

internal class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Host.UseWindowsService(options => options.ServiceName = "insight_web");
        builder.Host.UseSystemd();

        // LOGGING
        builder.Logging.ClearProviders();
        builder.Logging.SetMinimumLevel(LogLevel.Trace);
        builder.Logging.AddFilter("Microsoft.AspNetCore", LogLevel.Error);
        builder.Logging.AddFilter("MudBlazor", LogLevel.Warning);

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

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

        // BROKER
        builder.Services.AddBrokerServices(builder.Configuration);

        // REMOTE
        builder.Services.AddRemoteServices(builder.Configuration);

        // ASSETS
        builder.WebHost.UseStaticWebAssets();

        // IDENTITY
        builder.Services.AddIdentityServices(builder.Configuration);
        builder.Services.AddCustomAuthentication(builder.Configuration);
        builder.Services.AddSingleton<Bus>();

        // SECURITY
        builder.Services.AddAuthorization();

        // WEBSERVICES
        builder.Services.AddWebServices();
        builder.Services.AddProxyServices(builder.Configuration);
        builder.Services.AddRoutingServices(builder.Configuration);
        builder.Services.AddSignalR(options =>
        {
            options.EnableDetailedErrors = true;
            options.MaximumReceiveMessageSize = 1024 * 1024;
            //options.StreamBufferCapacity = 1024;
            options.EnableDetailedErrors = true;
        });

        // INFRASTRUCTURE
        builder.Services.AddDatabase(builder.Configuration);
        builder.Services.AddInfrastructureServices();

        // WEB:APP
        var app = builder.Build();

        // BASICS
        app.UseForwardedHeaders();
        //app.UseHttpsRedirection();

        // DEV-PROD SETTINGS
        if (app.Environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            //app.UseHsts();
        }

        // CORS
        //app.UseCors(x => x
        //    .AllowAnyOrigin()
        //    .AllowAnyMethod()
        //    .AllowAnyHeader());

        // ROUTING
        app.UseRouting();

        // SECURITY
        //app.UseAuthentication();
        app.UseAuthorization();

        // MIDDLEWARE
        app.UseMiddleware<IdentityMiddleware>();

        // BLAZOR HUBS (SIGNALR)
        app.MapBlazorHub();


        // FALLBACK
        app.MapFallbackToPage("/_Host");

        // STATIC FILES
        app.UseStaticFiles();

        // HOST START
        await app.RunAsync().ConfigureAwait(false);
    }
}

internal static class ServiceExtensions
{
    internal static IServiceCollection AddBrokerServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.UseHostedClient<WebSession, IMessage>(options =>
        {
            options.Host = configuration.GetValue<string?>(Appsettings.ServerHost) ?? throw new Exception($"{Appsettings.ServerHost} value not set (appsettings)");
            options.Port = configuration.GetValue<int?>(Appsettings.ServerPort) ?? throw new Exception($"{Appsettings.ServerPort} value not set (appsettings)");
            options.Keepalive = 10000;
            options.Timeout = 30000;
            options.Encryption = Encryption.Tls12;
            options.Compression = true;

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

        services.AddSingleton<IMessageHandler<WebSession>, AgentHandler>();

        return services;
    }

    internal static IServiceCollection AddRemoteServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.UseHostedServer<RemoteSession, IMessage>(options =>
        {
            options.Address = IPAddress.Any;
            options.Port = configuration.GetValue<int?>(Appsettings.RemoteServerPort) ?? throw new Exception($"{Appsettings.RemoteServerPort} 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.RemoteServerCertificate) ?? throw new Exception($"{Appsettings.RemoteServerCertificate} value not set (appsettings)");
            options.CertificatePassword = configuration.GetValue<string?>(Appsettings.RemoteServerCertificatePassword) ?? throw new Exception($"{Appsettings.RemoteServerCertificatePassword} value not set (appsettings)");

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

        services.AddSingleton<IMessageHandler<RemoteSession>, RemoteHandler>();

        return services;
    }
}