using System.Text;
using MassTransit;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Monitoring.Common;
using Monitoring.Notifications;
using Serilog;

const string hubPath = "/hubs/notifications";

try
{
	var builder = WebApplication.CreateBuilder(args);

	builder.AddServiceDefaults();

	AddLogging(builder);

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

	AddAuth(builder.Services, jwtSecret, jwtIssuer, jwtAudience);

	AddMassTransit(builder.Services);

	var signalRServerBuilder = builder.Services.AddSignalR();
	if (EnvironmentVariables.Has("REDIS_CONNECTION_STRING"))
	{
		var redisConnectionString = EnvironmentVariables.Get("REDIS_CONNECTION_STRING");
		signalRServerBuilder.AddStackExchangeRedis(redisConnectionString);
	}

	var app = builder.Build();

	// Configure the HTTP request pipeline.
	if (app.Environment.IsDevelopment())
	{
		app.MapOpenApi();
	}

	app.MapDefaultEndpoints();

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

	app.MapHub<NotificationHub>(hubPath);

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

	app.Run();
}
catch (Exception ex)
{
	Log.Fatal(ex, "Program terminated unexpectedly");
}

static void AddMassTransit(IServiceCollection services)
{
	var messageQueueUrl = EnvironmentVariables.Get("MESSAGE_QUEUE_URL");
	var messageUsername = EnvironmentVariables.Get("MESSAGE_QUEUE_USERNAME");
	var messagePassword = EnvironmentVariables.Get("MESSAGE_QUEUE_PASSWORD");

	Log.Information("AddMassTransit: {Url}", messageQueueUrl);

	services.AddMassTransit(config =>
	{
		config.SetKebabCaseEndpointNameFormatter();

		config.AddConsumersFromNamespaceContaining<NotificationSentConsumer>();

		config.UsingRabbitMq(
			(context, cfg) =>
			{
				cfg.Host(
					messageQueueUrl,
					h =>
					{
						h.Username(messageUsername);
						h.Password(messagePassword);
					}
				);

				cfg.ConfigureEndpoints(context);
			}
		);
	});
}

static void AddAuth(
	IServiceCollection services,
	string jwtSecret,
	string jwtIssuer,
	string jwtAudience
)
{
	services
		.AddAuthentication(options =>
		{
			options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
			options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
		})
		.AddJwtBearer(options =>
		{
			options.RequireHttpsMetadata = false; // Для локальной разработки можно отключить

			// Ключ и основные параметры валидации
			options.TokenValidationParameters = new TokenValidationParameters
			{
				ValidateIssuerSigningKey = true,
				IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)),

				ValidateIssuer = true,
				ValidIssuer = jwtIssuer,

				ValidateAudience = true,
				ValidAudience = jwtAudience,

				// При необходимости можно включить валидацию времени (exp, nbf)
				ValidateLifetime = true,
				ClockSkew = TimeSpan.Zero, // убрать дополнительный буфер 5 минут
			};

			// Если хотим, чтобы SignalR мог передавать токен через querystring при WebSocket:
			// (например, ?access_token=... )
			// Это нужно, если клиент не может передавать заголовок Authorization при WebSocket.
			// По умолчанию Bearer смотрит только заголовок, но можем разрешить и query:
			options.Events = new JwtBearerEvents
			{
				OnMessageReceived = context =>
				{
					// Извлекаем токен из cookie
					var cookieToken = context.Request.Cookies[CookieConstants.AccessToken];
					if (!string.IsNullOrEmpty(cookieToken))
					{
						context.Token = cookieToken;
						return Task.CompletedTask;
					}

					var accessToken = context.Request.Query["access_token"];
					var path = context.HttpContext.Request.Path;
					// Проверяем, относится ли это к нашему хабу
					if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments(hubPath))
					{
						context.Token = accessToken;
					}
					return Task.CompletedTask;
				},
			};
		});
}

static void AddLogging(WebApplicationBuilder builder)
{
	var loggerConfiguration = new LoggerConfiguration()
		.Enrich.FromLogContext()
		.Enrich.WithEnvironmentName()
		.Enrich.WithMachineName()
		.Enrich.WithProperty("ApplicationName", "Monitoring.Notifications")
		.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);
}
