using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Localization;
using MongoDB.Driver;
using QRRapidoApp.Configuration;
using QRRapidoApp.Data;
using QRRapidoApp.Middleware;
using QRRapidoApp.Providers;
using QRRapidoApp.Services;
using QRRapidoApp.Models.Ads;
using QRRapidoApp.Services.Ads;
using QRRapidoApp.Services.Monitoring;
using QRRapidoApp.Services.HealthChecks;
using StackExchange.Redis;
using Stripe;
using System.Globalization;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.OpenSearch;
using Serilog.Sinks.SystemConsole.Themes;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using AspNetCore.DataProtection.MongoDb;
using Microsoft.OpenApi.Models;

// Fix for WSL path issues - disable StaticWebAssets completely
var options = new WebApplicationOptions
{
    Args = args,
    ContentRootPath = Directory.GetCurrentDirectory(),
    WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")
};

// Disable StaticWebAssets for WSL compatibility
Environment.SetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUP__STATICWEBASSETS__ENABLED", "false");

var builder = WebApplication.CreateBuilder(options);

// Add Docker Secrets as configuration source (for Swarm deployments)
// Secrets override values from appsettings.json
builder.Configuration.AddDockerSecrets();

// Configure Serilog
var loggerConfig = new LoggerConfiguration()
    .ReadFrom.Configuration(builder.Configuration)
    .Enrich.FromLogContext()
    .Enrich.WithEnvironmentName()
    .Enrich.WithProcessId()
    .Enrich.WithThreadId()
    .Enrich.WithProperty("ApplicationName", builder.Configuration["ApplicationName"] ?? "QRRapido")
    .Enrich.WithProperty("Environment", builder.Environment.EnvironmentName)
    .WriteTo.Async(a => a.Console(
        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties:j}{NewLine}{Exception}",
        theme: AnsiConsoleTheme.Code));

var openSearchUrl = builder.Configuration["Serilog:OpenSearchUrl"];
if (!string.IsNullOrEmpty(openSearchUrl))
{
    var environment = builder.Environment.EnvironmentName.ToLower();
    var envMapping = environment switch
    {
        "Production" => "prod",
        "Staging" => "staging",
        "Development" => "dev",
        _ => environment
    };

    var indexFormat = $"qrrapido-logs-{envMapping}-{{0:yyyy-MM-dd}}";

    try
    {
        loggerConfig.WriteTo.Async(a => a.OpenSearch(new OpenSearchSinkOptions(new Uri(openSearchUrl))
        {
            IndexFormat = indexFormat,
            AutoRegisterTemplate = true,
            BufferBaseFilename = "./logs/buffer",
            ModifyConnectionSettings = conn => conn
                .RequestTimeout(TimeSpan.FromSeconds(30))
                .PingTimeout(TimeSpan.FromSeconds(10)),
            MinimumLogEventLevel = LogEventLevel.Information,
            EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog,
            RegisterTemplateFailure = RegisterTemplateRecovery.IndexAnyway,
            BatchPostingLimit = 50,
            Period = TimeSpan.FromSeconds(5),
        }), bufferSize: 10000, blockWhenFull: false);
    }
    catch (Exception ex)
    {
        // Fails silently, logs will continue on console.
        loggerConfig.WriteTo.Console(outputTemplate: $"Error setting up OpenSearch sink: {ex.Message}");
    }
}

Log.Logger = loggerConfig.CreateLogger();

builder.Host.UseSerilog();

// Add services to the container

builder.Services.AddControllersWithViews()
    .AddViewLocalization(Microsoft.AspNetCore.Mvc.Razor.LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();

builder.Services.AddDistributedMemoryCache(); // Armazena os dados da sess�o na mem�ria
builder.Services.AddSession(options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.IdleTimeout = TimeSpan.FromMinutes(30); // Tempo de expira��o
});

// Add HttpClient for health checks
builder.Services.AddHttpClient();

// MongoDB Configuration - optional for development
var mongoConnectionString = builder.Configuration.GetConnectionString("MongoDB");
if (!string.IsNullOrEmpty(mongoConnectionString))
{
    try
    {
        builder.Services.AddSingleton<IMongoClient>(serviceProvider =>
        {
            return new MongoClient(mongoConnectionString);
        });
        builder.Services.AddScoped<MongoDbContext>();
    }
    catch
    {
        // MongoDB not available - services will handle gracefully
        builder.Services.AddScoped<MongoDbContext>();
    }
}
else
{
    // Development mode without MongoDB
    builder.Services.AddScoped<MongoDbContext>();
}

builder.Services.AddMemoryCache();
builder.Services.AddSingleton<IDistributedCache, MemoryDistributedCacheWrapper>();
builder.Services.Configure<AdsConfigurationOptions>(builder.Configuration.GetSection("Ads"));
builder.Services.AddScoped<IAdSlotConfigurationProvider, ConfigurationAdSlotProvider>();

// ✅ DataProtection compartilhado via MongoDB (para múltiplas réplicas do Swarm)
if (!string.IsNullOrEmpty(mongoConnectionString))
{
    Log.Information("Configuring DataProtection to persist keys in MongoDB for Swarm compatibility");

    builder.Services.AddDataProtection()
        .SetApplicationName("QRRapido")
        .PersistKeysToMongoDb(
            () => new MongoClient(mongoConnectionString).GetDatabase("QRRapidoDB"),
            "DataProtectionKeys");
}
else
{
    // Fallback para FileSystem em desenvolvimento
    Log.Warning("MongoDB not available - using FileSystem for DataProtection (development only)");
    var keysDirectory = Path.Combine(Directory.GetCurrentDirectory(), "keys");
    Directory.CreateDirectory(keysDirectory);
    builder.Services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(keysDirectory))
        .SetApplicationName("QRRapido");
}


// Authentication Configuration
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Login";
        options.LogoutPath = "/Account/Logout";
        options.ExpireTimeSpan = TimeSpan.FromDays(30);
        options.SlidingExpiration = true;
    })
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];

        // ADICIONE ESTAS LINHAS:
        options.Events.OnRedirectToAuthorizationEndpoint = context =>
        {
            context.Response.Redirect(context.RedirectUri);
            return Task.CompletedTask;
        };

        // OU use este método alternativo:
        options.Scope.Add("email");
        options.Scope.Add("profile");
        options.SaveTokens = true;
    }).AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options =>
    {
        options.ClientId = builder.Configuration["Authentication:Microsoft:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Microsoft:ClientSecret"];

        options.AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
        options.TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token";

        // Força sempre mostrar a tela de seleção de conta
        options.Events.OnRedirectToAuthorizationEndpoint = context =>
        {
            context.Response.Redirect(context.RedirectUri + "&prompt=select_account");
            return Task.CompletedTask;
        };
    });

// Stripe Configuration
StripeConfiguration.ApiKey = builder.Configuration["Stripe:SecretKey"];

// Localization
builder.Services.AddLocalization(options => options.ResourcesPath = "");
//builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo("pt-BR"),
        new CultureInfo("es"),
        new CultureInfo("en"),
    };

    options.DefaultRequestCulture = new RequestCulture("pt-BR", "pt-BR");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    options.FallBackToParentCultures = false;
    options.FallBackToParentUICultures = false;

    // Clear default providers and add custom ones in priority order
    options.RequestCultureProviders.Clear();
    options.RequestCultureProviders.Add(new CustomRouteDataRequestCultureProvider());
    options.RequestCultureProviders.Add(new QueryStringRequestCultureProvider());
});

// Custom Services
builder.Services.AddScoped<IQRCodeService, QRRapidoService>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IPlanService, QRRapidoApp.Services.PlanService>();
builder.Services.AddScoped<IMarkdownService, MarkdownService>();
builder.Services.AddScoped<AdDisplayService>();
builder.Services.AddScoped<StripeService>();
builder.Services.AddScoped<LogoReadabilityAnalyzer>();
builder.Services.AddScoped<IQRBusinessManager, QRBusinessManager>();
builder.Services.AddScoped<IQuotaValidator, QuotaValidator>();
builder.Services.AddScoped<IApiRateLimitService, ApiRateLimitService>();

// Background Services
builder.Services.AddHostedService<HistoryCleanupService>();

// Monitoring Services
if (builder.Configuration.GetValue<bool>("ResourceMonitoring:Enabled", true))
{
    builder.Services.AddHostedService<ResourceMonitoringService>();
}

//if (builder.Configuration.GetValue<bool>("MongoDbMonitoring:Enabled", true))
//{
//    builder.Services.AddHostedService<MongoDbMonitoringService>();
//}

// CORS — two policies:
//   AllowSpecificOrigins: for the site (MVC endpoints)
//   ApiPolicy:            for /api/v1/* (open to any origin, auth is via API Key header)
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigins", policy =>
    {
        policy.WithOrigins("https://qrrapido.site", "https://www.qrrapido.site")
              .AllowAnyHeader()
              .AllowAnyMethod();
    });

    options.AddPolicy("ApiPolicy", policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader()
              .WithExposedHeaders(
                  "X-RateLimit-Limit",
                  "X-RateLimit-Remaining",
                  "X-RateLimit-Reset",
                  "X-Quota-Limit",
                  "X-Quota-Remaining",
                  "Retry-After");
    });
});
// Health checks with custom implementations
builder.Services.AddScoped<MongoDbHealthCheck>();
builder.Services.AddScoped<ResourceHealthCheck>();
builder.Services.AddScoped<ExternalServicesHealthCheck>();

builder.Services.AddHealthChecks()
    .AddCheck<MongoDbHealthCheck>("mongodb")
    .AddCheck<ResourceHealthCheck>("resources")
    .AddCheck<ExternalServicesHealthCheck>("external_services");

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Clear();
    options.KnownNetworks.Clear();
});

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = 429;
    options.AddFixedWindowLimiter("api", options =>
    {
        options.PermitLimit = 600; 
        options.Window = TimeSpan.FromMinutes(1);
        options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        options.QueueLimit = 10;
    });
});

builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.Limits.MaxConcurrentConnections = 2000;
    options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
});

// Swagger / OpenAPI — exposed at /api/docs
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title       = "QRRapido API",
        Version     = "v1",
        Description =
            "**QRRapido** — Ultra-fast QR Code generation API.\n\n" +
            "Generate QR codes for URLs, Pix payments, Wi-Fi, vCards, WhatsApp, SMS and more.\n\n" +
            "**PT-BR:** Gere QR codes para URLs, Pix, Wi-Fi, vCard, WhatsApp, SMS e muito mais.\n\n" +
            "**Authentication:** All endpoints (except `/ping`) require an API key sent in the `X-API-Key` header.\n" +
            "Get your key at [qrrapido.site/Developer](https://qrrapido.site/Developer).",
        Contact = new OpenApiContact
        {
            Name  = "QRRapido",
            Url   = new Uri("https://qrrapido.site"),
            Email = "contato@qrrapido.site"
        },
        License = new OpenApiLicense
        {
            Name = "Commercial — see qrrapido.site/terms"
        }
    });

    // API Key security scheme
    c.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
    {
        Type        = SecuritySchemeType.ApiKey,
        In          = ParameterLocation.Header,
        Name        = "X-API-Key",
        Description = "Your QRRapido API key. Obtain one at https://qrrapido.site/Developer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id   = "ApiKey"
                }
            },
            Array.Empty<string>()
        }
    });

    // Only document routes under /api/v1/ — ignore the internal web API controllers
    c.DocInclusionPredicate((_, apiDesc) =>
        apiDesc.RelativePath?.StartsWith("api/v1/", StringComparison.OrdinalIgnoreCase) == true);
});

var app = builder.Build();

app.UseRateLimiter();

app.UseForwardedHeaders();

// Swagger UI — accessible at /api/docs (no auth required, it's documentation)
app.UseSwagger(options =>
{
    options.RouteTemplate = "api/docs/{documentName}/openapi.json";
});
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/api/docs/v1/openapi.json", "QRRapido API v1");
    options.RoutePrefix  = "api/docs";
    options.DocumentTitle = "QRRapido API Docs";
});

// Configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
    // For API routes (/api/v1/*): return JSON errors — no stack traces
    app.UseExceptionHandler(errorApp =>
    {
        errorApp.Run(async context =>
        {
            if (!context.Response.HasStarted)
            {
                context.Response.ContentType = "application/json";
                context.Response.StatusCode  = 500;
                await context.Response.WriteAsJsonAsync(new
                {
                    error     = "An internal error occurred. Please try again later.",
                    requestId = context.TraceIdentifier
                });
            }
        });
    });
    app.UseHsts();
    app.UseHttpsRedirection();
}

// Security headers + JSON exception handler for /api/v1/* routes
app.UseMiddleware<QRRapidoApp.Middleware.ApiSecurityHeadersMiddleware>();

app.UseDefaultFiles(); // serve index.html para requisições de diretório (ex: /mcp → /mcp/index.html)
app.UseStaticFiles();

// Language redirection middleware (before routing)
app.UseMiddleware<LanguageRedirectionMiddleware>();

app.UseRouting();

// Localization middleware (after routing so route data is available)
app.UseRequestLocalization();

app.UseCors("AllowSpecificOrigins");

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

app.UseSession();

// Custom middleware
app.UseMiddleware<LastLoginUpdateMiddleware>();

// Health check endpoint
app.MapHealthChecks("/healthcheck");

//app.MapControllerRoute(
//    name: "auth",
//    pattern: "signin-{provider}",
//    defaults: new { controller = "Account", action = "ExternalLoginCallback" });

//app.MapControllerRoute(
//    name: "account",
//    pattern: "Account/{action}",
//    defaults: new { controller = "Account" });

// Language routes (must be first for WEB)
app.MapControllerRoute(
    name: "localized",
    pattern: "{culture:regex(^(pt-BR|es|en)$)}/{controller=Home}/{action=Index}/{id?}");

app.MapControllerRoute(
    name: "api_v1",
    pattern: "api/v1/{controller}/{action}/{id?}");

// Default fallback route (for development/testing without culture)
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

try
{
    Log.Information("Starting QRRapido application");
    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "QRRapido application terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}
