using System.Text;
using System.Threading.RateLimiting;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Serilog;
using SolutionErp.Api.Authorization;
using SolutionErp.Api.Hubs;
using SolutionErp.Api.Middleware;
using SolutionErp.Api.Services;
using SolutionErp.Application;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Identity;
using SolutionErp.Infrastructure;
using SolutionErp.Infrastructure.Identity;
using SolutionErp.Infrastructure.Persistence;

var builder = WebApplication.CreateBuilder(args);

// ---------- Logging (Serilog) — đọc config từ appsettings (Console dev, File prod) ----------
builder.Host.UseSerilog((ctx, cfg) => cfg
    .ReadFrom.Configuration(ctx.Configuration)
    .Enrich.FromLogContext());

// ---------- Core services ----------
// [S85 anh Kiệt — TZ FIX] Serialize DateTime UTC-with-Z. EF đọc datetime2 = Unspecified kind →
// default thiếu 'Z' → FE new Date(naive) hiểu là local → lệch +7h ("mới tạo báo 7h trước").
// UtcDateTimeJsonConverter ép 'Z' cho MỌI timestamp API → FE convert đúng về giờ local.
builder.Services.AddControllers().AddJsonOptions(o =>
    o.JsonSerializerOptions.Converters.Add(new SolutionErp.Api.Serialization.UtcDateTimeJsonConverter()));
builder.Services.AddSignalR();
builder.Services.AddSingleton<IRealtimeNotifier, SignalRNotifier>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUser, CurrentUserService>();
builder.Services.AddSingleton<SolutionErp.Application.Forms.IWebHostEnvironmentLocator, SolutionErp.Api.Services.WebHostEnvironmentLocator>();

builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);

// ---------- JWT auth ----------
var jwt = builder.Configuration.GetSection(JwtSettings.SectionName).Get<JwtSettings>()
    ?? throw new InvalidOperationException("Missing Jwt settings");

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.RequireHttpsMetadata = !builder.Environment.IsDevelopment();
        options.SaveToken = true;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = jwt.Issuer,
            ValidAudience = jwt.Audience,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwt.Secret)),
            ClockSkew = TimeSpan.FromMinutes(1),
        };
        // SignalR WebSockets can't set Authorization header — read JWT from
        // ?access_token=... query param when the request targets /hubs/*
        options.Events = new JwtBearerEvents
        {
            OnMessageReceived = ctx =>
            {
                var accessToken = ctx.Request.Query["access_token"];
                var path = ctx.HttpContext.Request.Path;
                if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
                    ctx.Token = accessToken;
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddScoped<IAuthorizationHandler, MenuPermissionHandler>();
builder.Services.AddAuthorization(opts =>
{
    foreach (var menu in MenuKeys.All)
    {
        foreach (var action in MenuKeys.Actions)
        {
            opts.AddPolicy($"{menu}.{action}", p =>
                p.Requirements.Add(new MenuPermissionRequirement(menu, action)));
        }
    }
});

// ---------- CORS — đọc origin từ config (Production) hoặc default dev ----------
var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>()
    ?? ["http://localhost:8080", "http://localhost:8082"];
builder.Services.AddCors(opts =>
{
    opts.AddDefaultPolicy(p => p
        .WithOrigins(allowedOrigins)
        .AllowAnyHeader()
        .AllowAnyMethod()
        .AllowCredentials());
});

// ---------- Rate limiting (Phase 5) ----------
var rateLimitAuth = builder.Configuration.GetValue<int?>("RateLimit:AuthLoginPerMinute") ?? 5;
var rateLimitGlobal = builder.Configuration.GetValue<int?>("RateLimit:GlobalPerMinute") ?? 300;

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;

    // Policy "auth-login" áp dụng [EnableRateLimiting] ở AuthController.Login
    options.AddPolicy("auth-login", httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
            factory: _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = rateLimitAuth,
                Window = TimeSpan.FromMinutes(1),
                QueueLimit = 0,
            }));

    // Global limit theo IP — chặn abuse
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown",
            factory: _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = rateLimitGlobal,
                Window = TimeSpan.FromMinutes(1),
                QueueLimit = 50,
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
            }));
});

// ---------- Health checks ----------
builder.Services.AddHealthChecks()
    .AddDbContextCheck<ApplicationDbContext>("database", HealthStatus.Unhealthy, ["ready"]);

// ---------- Swagger ----------
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "SolutionErp API", Version = "v1" });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "JWT Bearer token — nhập chỉ token, Swagger tự thêm 'Bearer '.",
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" },
            },
            Array.Empty<string>()
        },
    });
});

var app = builder.Build();

// ---------- Pipeline ----------
app.UseMiddleware<SecurityHeadersMiddleware>();
app.UseMiddleware<GlobalExceptionMiddleware>();
app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SolutionErp API v1"));
}
else
{
    app.UseHsts();
}

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

// Health check endpoints (IIS probe)
app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new HealthCheckOptions { Predicate = h => h.Tags.Contains("ready") });

app.MapControllers();
app.MapHub<NotificationHub>("/hubs/notifications");

// ---------- DB init + seed ----------
if (!args.Contains("--no-db-init"))
{
    try
    {
        await DbInitializer.InitializeAsync(app.Services);
    }
    catch (Exception ex)
    {
        app.Logger.LogError(ex, "DB initialization failed — app vẫn chạy, check connection string.");
    }
}

app.Run();
