using System.Text.Json.Serialization;
using HealthChecks.UI.Client;
using Managing.Api.Workers.Filters;
using Managing.Api.Workers.Workers;
using Managing.Application.Hubs;
using Managing.Bootstrap;
using Managing.Common;
using Managing.Core.Middleawares;
using Managing.Infrastructure.Databases.InfluxDb.Models;
using Managing.Infrastructure.Databases.MongoDb.Configurations;
using Managing.Infrastructure.Evm.Models.Privy;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.OpenApi.Models;
using NSwag;
using NSwag.Generation.Processors.Security;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.Elasticsearch;
using OpenApiSecurityRequirement = Microsoft.OpenApi.Models.OpenApiSecurityRequirement;
using OpenApiSecurityScheme = NSwag.OpenApiSecurityScheme;

// Builder
var builder = WebApplication.CreateBuilder(args);

builder.Configuration.SetBasePath(AppContext.BaseDirectory);
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json");

var mongoConnectionString = builder.Configuration.GetSection(Constants.Databases.MongoDb)["ConnectionString"];
var influxUrl = builder.Configuration.GetSection(Constants.Databases.InfluxDb)["Url"];
var web3ProxyUrl = builder.Configuration.GetSection("Web3Proxy")["BaseUrl"];

// Initialize Sentry
SentrySdk.Init(options =>
{
    // A Sentry Data Source Name (DSN) is required.
    options.Dsn = builder.Configuration["Sentry:Dsn"];

    // When debug is enabled, the Sentry client will emit detailed debugging information to the console.
    options.Debug = false;

    // Adds request URL and headers, IP and name for users, etc.
    options.SendDefaultPii = true;

    // This option is recommended. It enables Sentry's "Release Health" feature.
    options.AutoSessionTracking = true;

    // Enabling this option is recommended for client applications only. It ensures all threads use the same global scope.
    options.IsGlobalModeEnabled = false;

    // Example sample rate for your transactions: captures 10% of transactions
    options.TracesSampleRate = 0.1;

    options.Environment = builder.Environment.EnvironmentName;
});

// Add service discovery for Aspire
builder.Services.AddServiceDiscovery();

// Configure health checks
builder.Services.AddHealthChecks()
    .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
    .AddMongoDb(mongoConnectionString, name: "mongodb", tags: ["database"])
    .AddUrlGroup(new Uri($"{influxUrl}/health"), name: "influxdb", tags: ["database"])
    .AddUrlGroup(new Uri($"{web3ProxyUrl}/health"), name: "web3proxy", tags: ["api"]);

builder.WebHost.UseUrls("http://localhost:5001");

builder.Host.UseSerilog((hostBuilder, loggerConfiguration) =>
{
    var envName = builder.Environment.EnvironmentName.ToLower().Replace(".", "-");
    var indexFormat = $"managing-worker-{envName}-" + "{0:yyyy.MM.dd}";
    var yourTemplateName = "dotnetlogs";
    var es = new ElasticsearchSinkOptions(new Uri(hostBuilder.Configuration["ElasticConfiguration:Uri"]))
    {
        IndexFormat = indexFormat.ToLower(),
        AutoRegisterTemplate = true,
        OverwriteTemplate = true,
        TemplateName = yourTemplateName,
        AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
        TypeName = null,
        BatchAction = ElasticOpType.Create,
        MinimumLogEventLevel = LogEventLevel.Information,
        DetectElasticsearchVersion = true,
        RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
    };

    loggerConfiguration
        .WriteTo.Console()
        .WriteTo.Elasticsearch(es);
});
builder.Services.AddOptions();
builder.Services.Configure<ManagingDatabaseSettings>(builder.Configuration.GetSection(Constants.Databases.MongoDb));
builder.Services.Configure<InfluxDbSettings>(builder.Configuration.GetSection(Constants.Databases.InfluxDb));
builder.Services.Configure<PrivySettings>(builder.Configuration.GetSection(Constants.ThirdParty.Privy));
builder.Services.AddControllers().AddJsonOptions(options =>
    options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
    builder
        .SetIsOriginAllowed((host) => true)
        .AllowAnyOrigin()
        .WithOrigins("http://localhost:3000/")
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials();
}));
builder.Services.AddSignalR().AddJsonProtocol();

builder.Services.RegisterWorkersDependencies(builder.Configuration);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApiDocument(document =>
{
    document.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
    {
        Type = OpenApiSecuritySchemeType.ApiKey,
        Name = "Authorization",
        In = OpenApiSecurityApiKeyLocation.Header,
        Description = "Type into the textbox: Bearer {your JWT token}."
    });

    document.OperationProcessors.Add(
        new AspNetCoreOperationSecurityScopeProcessor("JWT"));
});
builder.Services.AddSwaggerGen(options =>
{
    options.SchemaFilter<EnumSchemaFilter>();
    options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
    {
        Description = "Please insert your JWT Token into field : Bearer {your_token}",
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        In = ParameterLocation.Header,
        Scheme = "Bearer",
        BearerFormat = "JWT"
    });
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new Microsoft.OpenApi.Models.OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] { }
        }
    });
});

builder.WebHost.SetupDiscordBot();
// builder.Services.AddHostedService<FeeWorker>();
// builder.Services.AddHostedService<PositionManagerWorker>();
// builder.Services.AddHostedService<PositionFetcher>();
// builder.Services.AddHostedService<PricesFiveMinutesWorker>();
builder.Services.AddHostedService<PricesFifteenMinutesWorker>();
builder.Services.AddHostedService<PricesOneHourWorker>();
builder.Services.AddHostedService<PricesFourHoursWorker>();
builder.Services.AddHostedService<PricesOneDayWorker>();
// builder.Services.AddHostedService<SpotlightWorker>();
// builder.Services.AddHostedService<TraderWatcher>();
// builder.Services.AddHostedService<LeaderboardWorker>();
// builder.Services.AddHostedService<FundingRatesWatcher>();

// App
var app = builder.Build();
app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}

app.UseOpenApi();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing Workers v1");
    c.RoutePrefix = string.Empty;
});

app.UseCors("CorsPolicy");

// Add Sentry diagnostics middleware (now using shared version from Core)
app.UseSentryDiagnostics();

// Using shared GlobalErrorHandlingMiddleware from Core project
app.UseMiddleware<GlobalErrorHandlingMiddleware>();

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHub<PositionHub>("/positionhub");

    endpoints.MapHealthChecks("/health", new HealthCheckOptions
    {
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });

    endpoints.MapHealthChecks("/alive", new HealthCheckOptions
    {
        Predicate = r => r.Tags.Contains("live"),
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });
});

app.Run();