using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.HttpOverrides;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Serilog;
using System.Diagnostics;
using System.Threading.RateLimiting;
using TakeoutSaaS.ApiGateway.Configuration;

const string CorsPolicyName = "GatewayCors";

// 1. 创建构建器并配置 Serilog
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, services, loggerConfiguration) =>
{
    loggerConfiguration
        .ReadFrom.Configuration(context.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext();
});

// 2. 配置 YARP 反向代理
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

// 3. 转发头部配置
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownIPNetworks.Clear();
    options.KnownProxies.Clear();
});

// 4. 配置 CORS
builder.Services.AddCors(options =>
{
    options.AddPolicy(CorsPolicyName, policy =>
    {
        policy.AllowAnyOrigin()
            .AllowAnyHeader()
            .AllowAnyMethod();
    });
});

// 5. 配置网关限流
builder.Services.Configure<GatewayRateLimitOptions>(builder.Configuration.GetSection("Gateway:RateLimiting"));
var rateLimitOptions = builder.Configuration.GetSection("Gateway:RateLimiting").Get<GatewayRateLimitOptions>() ?? new();

if (rateLimitOptions.Enabled)
{
    builder.Services.AddRateLimiter(options =>
    {
        options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
        options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(context =>
        {
            var remoteIp = context.Connection.RemoteIpAddress?.ToString() ?? "unknown";
            return RateLimitPartition.GetFixedWindowLimiter(remoteIp, _ => new FixedWindowRateLimiterOptions
            {
                PermitLimit = Math.Max(1, rateLimitOptions.PermitLimit),
                Window = TimeSpan.FromSeconds(Math.Max(1, rateLimitOptions.WindowSeconds)),
                QueueLimit = Math.Max(0, rateLimitOptions.QueueLimit),
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst
            });
        });
    });
}

// 6. 配置 OpenTelemetry
var otelOptions = builder.Configuration.GetSection("OpenTelemetry").Get<GatewayOpenTelemetryOptions>() ?? new();
if (otelOptions.Enabled)
{
    builder.Services.AddOpenTelemetry()
        // 1. 配置统一的 Resource，便于追踪定位。
        .ConfigureResource(resource => resource.AddService(otelOptions.ServiceName ?? "TakeoutSaaS.ApiGateway"))
        .WithMetrics(metrics =>
        {
            metrics.AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation()
                .AddRuntimeInstrumentation();

            if (!string.IsNullOrWhiteSpace(otelOptions.OtlpEndpoint))
            {
                metrics.AddOtlpExporter(options =>
                {
                    options.Endpoint = new Uri(otelOptions.OtlpEndpoint);
                });
            }
        })
        .WithTracing(tracing =>
        {
            tracing.AddAspNetCoreInstrumentation(options =>
                {
                    options.RecordException = true;
                })
                .AddHttpClientInstrumentation();

            if (!string.IsNullOrWhiteSpace(otelOptions.OtlpEndpoint))
            {
                tracing.AddOtlpExporter(options =>
                {
                    options.Endpoint = new Uri(otelOptions.OtlpEndpoint);
                });
            }
        });

    builder.Logging.AddOpenTelemetry(logging =>
    {
        logging.IncludeScopes = true;
        logging.ParseStateValues = true;
        if (!string.IsNullOrWhiteSpace(otelOptions.OtlpEndpoint))
        {
            logging.AddOtlpExporter(options =>
            {
                options.Endpoint = new Uri(otelOptions.OtlpEndpoint);
            });
        }
    });
}

// 7. 构建应用
var app = builder.Build();

// 8. 转发头中间件
app.UseForwardedHeaders();

// 9. 全局异常处理中间件
app.UseExceptionHandler(errorApp =>
{
    // 1. 捕获所有未处理异常并返回统一结构。
    errorApp.Run(async context =>
    {
        var feature = context.Features.Get<IExceptionHandlerFeature>();
        var traceId = Activity.Current?.Id ?? context.TraceIdentifier;
        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        context.Response.ContentType = "application/json";

        var payload = new
        {
            success = false,
            code = 500,
            message = "Gateway internal error",
            traceId
        };

        var logger = context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger("Gateway");
        logger.LogError(feature?.Error, "网关异常 {TraceId}", traceId);
        await context.Response.WriteAsJsonAsync(payload, cancellationToken: context.RequestAborted);
    });
});

// 10. 请求日志
app.UseSerilogRequestLogging(options =>
{
    options.MessageTemplate = "网关请求 {RequestMethod} {RequestPath} => {StatusCode} 用时 {Elapsed:0.000} 秒";
    options.GetLevel = (httpContext, elapsed, ex) => ex is not null ? Serilog.Events.LogEventLevel.Error : Serilog.Events.LogEventLevel.Information;
    options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
    {
        diagnosticContext.Set("TraceId", Activity.Current?.Id ?? httpContext.TraceIdentifier);
        diagnosticContext.Set("ClientIp", httpContext.Connection.RemoteIpAddress?.ToString());
    };
});

// 11. CORS 与限流
app.UseCors(CorsPolicyName);

if (rateLimitOptions.Enabled)
{
    app.UseRateLimiter();
}

// 12. 透传请求头并保证 Trace
app.Use(async (context, next) =>
{
    // 1. 确保请求拥有可追踪的 ID。
    if (!context.Request.Headers.ContainsKey("X-Request-Id"))
    {
        context.Request.Headers["X-Request-Id"] = Activity.Current?.Id ?? Guid.NewGuid().ToString("N");
    }

    // 2. 透传租户等标识头，方便下游继续使用。
    var tenantId = context.Request.Headers["X-Tenant-Id"];
    if (!string.IsNullOrWhiteSpace(tenantId))
    {
        context.Request.Headers["X-Tenant-Id"] = tenantId;
    }

    var tenantCode = context.Request.Headers["X-Tenant-Code"];
    if (!string.IsNullOrWhiteSpace(tenantCode))
    {
        context.Request.Headers["X-Tenant-Code"] = tenantCode;
    }

    await next(context);
});

// 13. 映射反向代理与健康接口
app.MapReverseProxy();

app.MapGet("/", () => Results.Json(new
{
    Service = "TakeoutSaaS.ApiGateway",
    Status = "OK",
    Timestamp = DateTimeOffset.UtcNow
}));

app.MapGet("/healthz", () => Results.Json(new
{
    Service = "TakeoutSaaS.ApiGateway",
    Status = "Healthy",
    Timestamp = DateTimeOffset.UtcNow
}));

app.Run();
