using System.Reflection;
using MaxMind.GeoIP2;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Nostr.Client.Client;
using NostrRelay;
using NostrServices.Services;
using NostrServices.Services.EventHandlers;
using Prometheus;
using StackExchange.Redis;

namespace NostrServices;

public static class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        var config = builder.Configuration.GetSection("Config").Get<Config>()!;

        var cx = await ConnectionMultiplexer.ConnectAsync(config.Redis);
        builder.Services.AddSingleton(cx);

        builder.Services.AddSingleton<IGeoIP2DatabaseReader>(_ => new DatabaseReader(config.GeoIpDatabase));
        builder.Services.AddSingleton<NostrMultiWebsocketClient>();
        builder.Services.AddSingleton<INostrClient>(s => s.GetRequiredService<NostrMultiWebsocketClient>());
        builder.Services.AddSingleton<NostrListener>();
        builder.Services.AddHostedService<RelayListener>();

        builder.Services.AddTransient<IDatabase>(svc => svc.GetRequiredService<ConnectionMultiplexer>().GetDatabase());
        builder.Services.AddTransient<ISubscriber>(svc => svc.GetRequiredService<ConnectionMultiplexer>().GetSubscriber());
        builder.Services.AddTransient<RedisStore>();
        builder.Services.AddTransient<CacheRelay>();
        builder.Services.AddNostrEventHandlers();
        builder.Services.AddHostedService<NostrListener.NostrListenerLifetime>();
        //builder.Services.AddHostedService<RelayScraperService>();
        //builder.Services.AddHostedService<PubkeyStatsService>();

        builder.Services.AddControllers().AddNewtonsoftJson(opt =>
        {
            opt.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            opt.SerializerSettings.Formatting = Formatting.None;
            opt.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
            opt.SerializerSettings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor;
            opt.SerializerSettings.Converters =
            [
                new UnixDateTimeConverter()
            ];

            opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
        });

        builder.Services.AddEndpointsApiExplorer();
        builder.Services.AddSwaggerGen(opt =>
        {
            opt.SwaggerDoc("v1", new OpenApiInfo
            {
                Version = "v1",
                Title = "NostrServices",
                Description = "Nostr services",
                Contact = new OpenApiContact
                {
                    Name = "v0l",
                    Url = new Uri("https://snort.social/kieran")
                },
                License = new OpenApiLicense
                {
                    Name = "MIT",
                    Url = new Uri("https://git.v0l.io/Kieran/NostrServices")
                }
            });

            var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            opt.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
        });

        builder.Services.AddHttpClient();
        builder.Services.AddMemoryCache();
        builder.Services.AddResponseCaching();
        builder.Services.AddMetrics();
        builder.Services.AddHealthChecks();
        builder.Services.AddCors();


        var app = builder.Build();
        app.UseResponseCaching();
        app.UseSwagger();
        app.UseSwaggerUI();
        app.UseHttpMetrics();
        app.UseHealthChecks("/healthz");
        app.UseWebSockets();
        app.UseCors(o =>
        {
            o.AllowAnyOrigin();
            o.AllowAnyHeader();
            o.AllowAnyMethod();
        });

        app.UseRouting();
        app.MapControllers();
        app.MapMetrics();
        app.MapRelay<CacheRelay>("/");

        await app.RunAsync();
    }
}
