using System.Text;
using FluentValidation;
using JasperFx;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Monitoring.Common;
using Monitoring.Features.AlarmManagement;
using Monitoring.Features.Reports;
using Monitoring.Infrastructure.Data;
using Monitoring.Infrastructure.Services;
using Monitoring.Models.Binders;
using Monitoring.Notifications.Client;
using Monitoring.Repositories;
using Monitoring.SchemaTransformers;
using Monitoring.Services;
using Scalar.AspNetCore;
using Serilog;
using Wolverine;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();

builder.AddServiceDefaults();

builder.Services.AddNotificationService();

builder.Services.AddControllers(options =>
{
	options.ModelBinderProviders.Insert(0, new TableSortModelBinderProvider());
});
builder.Services.AddSignalR(options =>
{
	if (builder.Environment.IsDevelopment())
	{
		options.EnableDetailedErrors = true;
	}

	options.KeepAliveInterval = TimeSpan.FromSeconds(10);
	options.HandshakeTimeout = TimeSpan.FromSeconds(5);
});

builder.Services.AddOpenApi(options =>
{
	options.AddSchemaTransformer(new FixCountOfEventsSchemaTransformer());
	options.AddSchemaTransformer(new BinaryResponseSchemaTransformer());
	// options.AddSchemaTransformer(new EnumSchemaTransformer());
});

AddLogging(builder);
static void AddLogging(WebApplicationBuilder builder)
{
	var loggerConfiguration = new LoggerConfiguration()
		.Enrich.FromLogContext()
		.Enrich.WithEnvironmentName()
		.Enrich.WithMachineName()
		.Enrich.WithProperty("ApplicationName", "Monitoring")
		.WriteTo.Console();
	var logLevel = LogLevel.Information;

	if (EnvironmentVariables.Has("LOG_LEVEL"))
	{
		switch (EnvironmentVariables.Get("LOG_LEVEL"))
		{
			case "DEBUG":
				loggerConfiguration = loggerConfiguration.MinimumLevel.Debug();
				logLevel = LogLevel.Debug;
				break;

			case "INFO":
				loggerConfiguration = loggerConfiguration.MinimumLevel.Information();
				logLevel = LogLevel.Information;
				break;

			case "WARN":
				loggerConfiguration = loggerConfiguration.MinimumLevel.Warning();
				logLevel = LogLevel.Warning;
				break;

			case "ERROR":
				loggerConfiguration = loggerConfiguration.MinimumLevel.Error();
				logLevel = LogLevel.Error;
				break;

			default:
				break;
		}
	}

	//if (EnvironmentVariables.Has("LOGSTASH_URL"))
	//{
	//	var logstashUrl = EnvironmentVariables.Get("LOGSTASH_URL");
	//	var prefix = EnvironmentVariables.Has("LOGSTASH_PREFIX")
	//		? $"{EnvironmentVariables.Get("LOGSTASH_PREFIX")}-"
	//		: string.Empty;
	//	loggerConfiguration = loggerConfiguration.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(logstashUrl))
	//	{
	//		AutoRegisterTemplate = true,
	//		AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv7,
	//		IndexFormat = $"{prefix}logs-{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")?.ToLower().Replace(".", "-")}-{DateTime.UtcNow:yyyy-MM}",
	//		NumberOfReplicas = 1,
	//		NumberOfShards = 2
	//	});
	//}

	Log.Logger = loggerConfiguration.CreateLogger();
	builder.Host.UseSerilog();

	builder.Logging.SetMinimumLevel(logLevel);
}

var jwtIssuer = EnvironmentVariables.Get("JWT_ISSUER");
var jwtAudience = EnvironmentVariables.Get("JWT_AUDIENCE");
var jwtKey = EnvironmentVariables.Get("JWT_KEY");

builder.Services.AddCors(options =>
{
	options.AddDefaultPolicy(builder =>
	{
		if (EnvironmentVariables.Has("CLIENT_URL_ADDRESS"))
		{
			var clientAddress = EnvironmentVariables.Get("CLIENT_URL_ADDRESS");
			Log.Warning("Origins: {address}", clientAddress);
			builder.WithOrigins($"http://{clientAddress}", $"https://{clientAddress}");
		}
		else
		{
			_ = builder.WithOrigins(
				$"http://localhost:3000",
				"http://localhost:3000",
				"http://localhost:5000"
			);
		}

		builder.AllowAnyHeader().AllowAnyMethod().AllowCredentials();
	});
});

AddJWTAuthentication(builder.Services, jwtIssuer, jwtAudience, jwtKey);
static IServiceCollection AddJWTAuthentication(
	IServiceCollection services,
	string issuer,
	string audience,
	string key
)
{
	_ = services
		.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
		.AddJwtBearer(
			JwtBearerDefaults.AuthenticationScheme,
			options =>
			{
				options.RequireHttpsMetadata = false;
				options.SaveToken = true;
				options.TokenValidationParameters = new TokenValidationParameters
				{
					ValidateActor = true,
					ValidateIssuer = true,
					ValidateAudience = true,
					RequireExpirationTime = true,
					ValidateLifetime = true,
					ValidateIssuerSigningKey = true,
					ValidIssuer = issuer,
					ValidAudience = audience,
					IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
					ClockSkew = TimeSpan.Zero, // Отключение сдвига по времени для строгой проверки срока действия токена
				};

				options.Events = new JwtBearerEvents
				{
					OnMessageReceived = context =>
					{
						if (!string.IsNullOrEmpty(context.Token))
						{
							return Task.CompletedTask;
						}

						// Извлекаем токен из cookie
						var cookieToken = context.Request.Cookies[CookieConstants.AccessToken];
						if (!string.IsNullOrEmpty(cookieToken))
						{
							context.Token = cookieToken;
							return Task.CompletedTask;
						}

						// Фоллбэк: токен из query для SignalR
						var accessToken = context.Request.Query["access_token"];
						var path = context.HttpContext.Request.Path;
						if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
						{
							context.Token = accessToken;
						}

						return Task.CompletedTask;
					},
					OnAuthenticationFailed = context =>
					{
						Log.Warning("OnAuthenticationFailed: {context}", context);

						return Task.CompletedTask;
					},
					OnForbidden = context =>
					{
						Log.Warning("OnForbidden: {context}", context);

						return Task.CompletedTask;
					},
					OnTokenValidated = context =>
					{
						return Task.CompletedTask;
					},
				};
			}
		);

	_ = services.AddAuthorization();

	return services;
}

if (EnvironmentVariables.Has("DATABASE_ASPIRE_NAME"))
{
	var connectionName = EnvironmentVariables.Get("DATABASE_ASPIRE_NAME");
	builder.AddNpgsqlDbContext<DataContext>(connectionName: connectionName);
}
else
{
	var connectionString = EnvironmentVariables.Get("DATABASE_CONNECTION_STRING");
	builder.Services.AddNpgsql<DataContext>(connectionString);
}
builder
	.Services.AddScoped<IDataContext>(sp => sp.GetRequiredService<DataContext>())
	.AddScoped<AlarmScopeTransitionMatrix>();

builder.Services.AddServices(jwtIssuer, jwtAudience, jwtKey);
builder.Services.AddInfrastructureServices();
builder.Services.AddRepositories();
builder.Services.AddScoped<ReportRepository>();
builder.Services.AddScoped<FileBuilderFactory>();

builder.Services.AddValidatorsFromAssemblyContaining<Program>();

builder.Services.AddMemoryCache();

builder.Services.AddNotificationClient();

builder.Host.UseWolverine();

var app = builder.Build();

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

app.UseAssetConcurrencyHandling();

app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
	app.MapOpenApi();
	app.MapScalarApiReference();
	app.UseSwaggerUI(c =>
	{
		c.SwaggerEndpoint($"/openapi/v1.json", "Monitoring API v1");
		c.RoutePrefix = "swagger";
	});
}

// app.UseApiCommon(app.Environment, true);
app.UseRouting();
app.UseCors();

app.UseAuthentication();
app.UseAuthorization();

await MigrateDatabase(app);
static async Task MigrateDatabase(WebApplication app)
{
	using var scope = app.Services.CreateScope();
	var context = scope.ServiceProvider.GetRequiredService<DataContext>();
	await context.Database.MigrateAsync();
}

app.MapControllers();

//app.UseEndpoints();

app.MapDefaultEndpoints();

app.UseRequestTimeouts();
app.UseOutputCache();

return await app.RunJasperFxCommands(args);
