using System.Threading.RateLimiting;
using FastEndpoints;
using FastEndpoints.Security;
using FastEndpoints.Swagger;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Serilog.Events;
using TrackQrApi.Data;
using TrackQrApi.Features.Assets.Services;
using TrackQrApi.Features.Auth.Settings;
using TrackQrApi.Features.Billing.Services;
using TrackQrApi.Features.Billing.Settings;
using TrackQrApi.Features.Email.Services;
using TrackQrApi.Features.Events.Services;
using TrackQrApi.Features.Plans.Services;
using TrackQrApi.Features.QRCodes.Services;
using TrackQrApi.Middleware;

// Configure Serilog
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
    .CreateLogger();

try
{
    Log.Information("Starting TrakQR API");

    var builder = WebApplication.CreateBuilder(args);

    // Use Serilog
    builder.Host.UseSerilog();

    // Configure CORS
    builder.Services.AddCors(options =>
    {
        options.AddDefaultPolicy(policy =>
        {
            if (builder.Environment.IsDevelopment())
            {
                policy.SetIsOriginAllowed(origin => new Uri(origin).IsLoopback)
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            }
            else
            {
                // Production: configure allowed origins from config
                var allowedOrigins = builder.Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>()
                                     ?? ["https://trakqr.com"];
                policy.WithOrigins(allowedOrigins)
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            }
        });
    });

    // Configure Rate Limiting (skip in Testing environment)
    var isTestingEnvironment = builder.Environment.EnvironmentName == "Testing";
    builder.Services.AddRateLimiter(options =>
    {
        options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

        // Use very high limits in testing environment
        var authLimit = isTestingEnvironment ? 100000 : 10;
        var globalLimit = isTestingEnvironment ? 100000 : 100;
        var redirectLimit = isTestingEnvironment ? 100000 : 1000;
        var apiLimit = isTestingEnvironment ? 100000 : 200;

        // Global rate limit for all endpoints
        options.AddPolicy("global", context =>
            RateLimitPartition.GetFixedWindowLimiter(
                context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
                _ => new FixedWindowRateLimiterOptions
                {
                    PermitLimit = globalLimit,
                    Window = TimeSpan.FromMinutes(1),
                    QueueLimit = 0
                }));

        // Strict rate limit for authentication endpoints
        options.AddPolicy("auth", context =>
            RateLimitPartition.GetFixedWindowLimiter(
                context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
                _ => new FixedWindowRateLimiterOptions
                {
                    PermitLimit = authLimit,
                    Window = TimeSpan.FromMinutes(1),
                    QueueLimit = 0
                }));

        // Higher limit for redirect endpoint (public, needs to be fast)
        options.AddPolicy("redirect", context =>
            RateLimitPartition.GetSlidingWindowLimiter(
                context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
                _ => new SlidingWindowRateLimiterOptions
                {
                    PermitLimit = redirectLimit,
                    Window = TimeSpan.FromMinutes(1),
                    SegmentsPerWindow = 4,
                    QueueLimit = 0
                }));

        // API rate limit for authenticated endpoints
        options.AddPolicy("api", context =>
            RateLimitPartition.GetTokenBucketLimiter(
                context.User?.Identity?.Name ??
                context.Connection.RemoteIpAddress?.ToString() ?? "unknown",
                _ => new TokenBucketRateLimiterOptions
                {
                    TokenLimit = apiLimit,
                    ReplenishmentPeriod = TimeSpan.FromMinutes(1),
                    TokensPerPeriod = apiLimit,
                    QueueLimit = 0
                }));
    });

    // Add services to the container
    builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseNpgsql(builder.Configuration.GetConnectionString("PostgresConnection")));

    // Register application services
    builder.Services.AddSingleton<IGeoIpService, GeoIpService>();
    builder.Services.AddSingleton<IEventTrackingService, EventTrackingService>();
    builder.Services.AddSingleton<IQrCodeGeneratorService, QrCodeGeneratorService>();
    builder.Services.AddSingleton<IAssetStorageService, LocalAssetStorageService>();
    builder.Services.AddSingleton<IPlanLimitsService, PlanLimitsService>();

    // Configure email service
    builder.Services.Configure<EmailSettings>(builder.Configuration.GetSection("Email"));
    var emailProvider = builder.Configuration.GetValue<string>("Email:Provider") ?? "console";
    if (emailProvider == "smtp")
        builder.Services.AddSingleton<IEmailService, SmtpEmailService>();
    else
        // Use console email service for development
        builder.Services.AddSingleton<IEmailService, ConsoleEmailService>();

    // Configure Stripe
    builder.Services.Configure<StripeSettings>(builder.Configuration.GetSection("Stripe"));
    builder.Services.AddSingleton<IStripeService, StripeService>();

    // Configure JWT settings
    builder.Services.Configure<JwtSettings>(builder.Configuration.GetSection("Jwt"));
    var jwtSettings = builder.Configuration.GetSection("Jwt").Get<JwtSettings>()!;

    // Configure authentication
    builder.Services.AddAuthenticationJwtBearer(
            signing => signing.SigningKey = jwtSettings.Secret,
            bearer =>
            {
                bearer.TokenValidationParameters.ValidIssuer = jwtSettings.Issuer;
                bearer.TokenValidationParameters.ValidAudience = jwtSettings.Audience;
            });
    builder.Services.AddAuthorization();
    builder.Services.AddFastEndpoints();
    builder.Services.SwaggerDocument();

    var app = builder.Build();

    // Global error handling middleware (must be first)
    app.UseMiddleware<GlobalExceptionMiddleware>();

    // Request logging middleware
    app.UseSerilogRequestLogging(options =>
    {
        options.MessageTemplate = "{RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms";
        options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
        {
            diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
            diagnosticContext.Set("UserAgent", httpContext.Request.Headers.UserAgent.ToString());
            diagnosticContext.Set("ClientIP", httpContext.Connection.RemoteIpAddress?.ToString());
        };
    });

    app.UseCors();
    app.UseRateLimiter();
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseFastEndpoints();
    if (app.Environment.IsDevelopment())
    {
        app.UseSwaggerGen();
    }

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