using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
using MassTransit;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Serilog;
using StackExchange.Redis;
using TakeoutSaaS.Application.App.Common.Geo;
using TakeoutSaaS.Application.App.Extensions;
using TakeoutSaaS.Application.App.Members.MessageReach.Options;
using TakeoutSaaS.Application.App.Members.MessageReach.Services;
using TakeoutSaaS.Application.Dictionary.Extensions;
using TakeoutSaaS.Application.Identity.Extensions;
using TakeoutSaaS.Application.Messaging.Extensions;
using TakeoutSaaS.Application.Sms.Extensions;
using TakeoutSaaS.Application.Storage.Extensions;
using TakeoutSaaS.Infrastructure.App.Extensions;
using TakeoutSaaS.Infrastructure.Dictionary.Extensions;
using TakeoutSaaS.Infrastructure.Identity.Extensions;
using TakeoutSaaS.Infrastructure.App.Persistence;
using TakeoutSaaS.Module.Authorization.Extensions;
using TakeoutSaaS.Module.Messaging.Extensions;
using TakeoutSaaS.Module.Messaging.Options;
using TakeoutSaaS.Module.Scheduler.Extensions;
using TakeoutSaaS.Module.Sms.Extensions;
using TakeoutSaaS.Module.Storage.Extensions;
using TakeoutSaaS.Module.Tenancy.Extensions;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Serialization;
using TakeoutSaaS.Shared.Web.Extensions;
using TakeoutSaaS.Shared.Web.Filters;
using TakeoutSaaS.Shared.Web.Security;
using TakeoutSaaS.Shared.Web.Swagger;
using TakeoutSaaS.TenantApi.Consumers;
using TakeoutSaaS.TenantApi.Hubs;
using TakeoutSaaS.TenantApi.Options;
using TakeoutSaaS.TenantApi.Services;

// 1. 创建构建器与日志模板
var builder = WebApplication.CreateBuilder(args);
const string logTemplate = "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} {Level:u3}] [TraceId:{TraceId}] [SpanId:{SpanId}] [Service:{Service}] {SourceContext} {Message:lj}{NewLine}{Exception}";
var isDevelopment = builder.Environment.IsDevelopment();

// 2. 配置 Serilog
builder.Host.UseSerilog((_, _, configuration) =>
{
    configuration
        .Enrich.FromLogContext()
        .Enrich.WithProperty("Service", "TenantApi")
        .WriteTo.Console(outputTemplate: logTemplate)
        .WriteTo.File(
            "logs/tenant-api-.log",
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 7,
            shared: true,
            outputTemplate: logTemplate);
});

// 3. 注册 Web Core（控制器、API 版本化、模型验证、雪花 ID 序列化等）
builder.Services.AddHttpContextAccessor();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddScoped<ICurrentUserAccessor, HttpContextCurrentUserAccessor>();
builder.Services
    .AddControllers(options =>
    {
        options.Filters.Add<ValidateModelAttribute>();
        options.Filters.Add<ApiResponseResultFilter>();
    })
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new SnowflakeIdJsonConverter());
        options.JsonSerializerOptions.Converters.Add(new NullableSnowflakeIdJsonConverter());
    });
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});
var apiVersioningBuilder = builder.Services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
});
apiVersioningBuilder.AddApiExplorer(setup =>
{
    setup.GroupNameFormat = "'v'VVV";
    setup.SubstituteApiVersionInUrl = true;
});

// 4. (空行后) 开发环境启用 Swagger（含 JWT 鉴权按钮）
if (isDevelopment)
{
    builder.Services.AddSharedSwagger(options =>
    {
        options.Title = "外卖SaaS - 租户管理端";
        options.Description = "租户管理员 API 文档";
        options.EnableAuthorization = true;
    });
}

// 5. 注册鉴权授权与权限策略
builder.Services.AddJwtAuthentication(builder.Configuration);
builder.Services.AddAuthorization();
builder.Services.AddPermissionAuthorization();
builder.Services.AddHealthChecks();

// 5.1 注册 SignalR + Redis Backplane
var signalRBuilder = builder.Services.AddSignalR()
    .AddJsonProtocol(options =>
    {
        options.PayloadSerializerOptions.Converters.Add(new SnowflakeIdJsonConverter());
    });
var redisConn = builder.Configuration.GetConnectionString("Redis");
if (!string.IsNullOrWhiteSpace(redisConn))
{
    signalRBuilder.AddStackExchangeRedis(redisConn, opt =>
        opt.Configuration.ChannelPrefix = RedisChannel.Literal("takeout-signalr"));
}

// 6. 注册应用层与基础设施（仅租户侧所需）
builder.Services.AddAppApplication();
builder.Services.AddSmsApplication(builder.Configuration);
builder.Services.AddIdentityApplication(enableMiniSupport: false);
builder.Services.AddAppInfrastructure(builder.Configuration);
builder.Services.AddIdentityInfrastructure(builder.Configuration, enableMiniFeatures: false, enableAdminSeed: false);

// 7. 注册多租户解析（依赖 ITenantRepository，需在 Infrastructure 之后）
builder.Services.AddTenantResolution(builder.Configuration);

// 8. 注册字典模块（系统参数、字典项、缓存等）
builder.Services.AddDictionaryApplication();
builder.Services.AddDictionaryInfrastructure(builder.Configuration);

// 9. 注册消息发布能力（未配置 RabbitMQ 时自动降级为 NoOp 实现）
builder.Services.AddMessagingApplication();
builder.Services.AddMessagingModule(builder.Configuration);
builder.Services.AddSmsModule(builder.Configuration);
builder.Services.AddMassTransit(configurator =>
{
    // 注册 SignalR 推送消费者
    configurator.AddConsumer<OrderCreatedConsumer>();
    configurator.AddConsumer<OrderStatusChangedConsumer>();
    configurator.AddConsumer<OrderUrgeConsumer>();
    configurator.AddConsumer<PaymentSucceededConsumer>();

    configurator.AddEntityFrameworkOutbox<TakeoutAppDbContext>(outbox =>
    {
        outbox.UsePostgres();
        outbox.UseBusOutbox();
    });

    configurator.UsingRabbitMq((context, cfg) =>
    {
        var options = builder.Configuration.GetSection("RabbitMQ").Get<RabbitMqOptions>()
            ?? throw new InvalidOperationException("缺少 RabbitMQ 配置。");

        var virtualHost = string.IsNullOrWhiteSpace(options.VirtualHost) ? "/" : options.VirtualHost.Trim();
        var virtualHostPath = virtualHost == "/" ? "/" : $"/{virtualHost.TrimStart('/')}";
        var hostUri = new Uri($"rabbitmq://{options.Host}:{options.Port}{virtualHostPath}");
        cfg.Host(hostUri, host =>
        {
            host.Username(options.Username);
            host.Password(options.Password);
        });

        cfg.PrefetchCount = options.PrefetchCount;
        cfg.ConfigureEndpoints(context);
    });
});
builder.Services.AddStorageModule(builder.Configuration);
builder.Services.AddStorageApplication();
builder.Services.AddSchedulerModule(builder.Configuration);
builder.Services.AddOptions<MemberMessagingOptions>()
    .Bind(builder.Configuration.GetSection("MemberMessaging"))
    .ValidateDataAnnotations()
    .ValidateOnStart();
builder.Services.AddHttpClient<IMemberMessageWeChatSender, MemberMessageWeChatSender>(client =>
{
    client.BaseAddress = new Uri("https://api.weixin.qq.com/");
    client.Timeout = TimeSpan.FromSeconds(10);
});
builder.Services.AddScoped<MemberMessageReachDispatchJobRunner>();

// 9.1 注册腾讯地图地理编码服务（服务端签名）
builder.Services.Configure<TencentMapOptions>(builder.Configuration.GetSection(TencentMapOptions.SectionName));
builder.Services.AddHttpClient(TencentMapGeocodingService.HttpClientName, client =>
{
    client.Timeout = TimeSpan.FromSeconds(8);
});
builder.Services.AddScoped<TencentMapGeocodingService>();
builder.Services.AddScoped<IAddressGeocodingService>(provider => provider.GetRequiredService<TencentMapGeocodingService>());
builder.Services.AddScoped<GeoLocationOrchestrator>();
builder.Services.AddScoped<ProductSkuSaveService>();
builder.Services.AddScoped<ProductSkuSaveJobRunner>();
builder.Services.AddHostedService<GeoLocationRetryBackgroundService>();

// 10. 配置 OpenTelemetry 采集
var otelSection = builder.Configuration.GetSection("Otel");
var otelEndpoint = otelSection.GetValue<string>("Endpoint");
var useConsoleExporter = otelSection.GetValue<bool?>("UseConsoleExporter") ?? builder.Environment.IsDevelopment();
builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource.AddService(
        serviceName: "TakeoutSaaS.TenantApi",
        serviceVersion: "1.0.0",
        serviceInstanceId: Environment.MachineName))
    .WithTracing(tracing =>
    {
        tracing
            .SetSampler(new ParentBasedSampler(new AlwaysOnSampler()))
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddEntityFrameworkCoreInstrumentation();
        if (!string.IsNullOrWhiteSpace(otelEndpoint))
        {
            tracing.AddOtlpExporter(exporter =>
            {
                exporter.Endpoint = new Uri(otelEndpoint);
            });
        }
        if (useConsoleExporter)
        {
            tracing.AddConsoleExporter();
        }
    })
    .WithMetrics(metrics =>
    {
        metrics
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddRuntimeInstrumentation()
            .AddPrometheusExporter();
        if (!string.IsNullOrWhiteSpace(otelEndpoint))
        {
            metrics.AddOtlpExporter(exporter =>
            {
                exporter.Endpoint = new Uri(otelEndpoint);
            });
        }
        if (useConsoleExporter)
        {
            metrics.AddConsoleExporter();
        }
    });

// 11. 配置 CORS
var tenantOrigins = ResolveCorsOrigins(builder.Configuration, "Cors:Tenant");
builder.Services.AddCors(options =>
{
    options.AddPolicy("TenantApiCors", policy =>
    {
        ConfigureCorsPolicy(policy, tenantOrigins);
    });
});

// 12. 构建应用并配置中间件管道
var app = builder.Build();
app.UseCors("TenantApiCors");

// 1. (空行后) 先完成身份认证，确保租户解析优先使用 Token Claim
app.UseAuthentication();

// 2. (空行后) 解析并注入租户上下文（已认证请求不允许 Header 覆盖）
app.UseTenantResolution();

// 3. (空行后) 通用 Web Core 中间件（异常、ProblemDetails、日志等）
app.UseSharedWebCore();

// 4. (空行后) 执行授权
app.UseAuthorization();
app.UseSchedulerDashboard(builder.Configuration);

// 5. (空行后) 开发环境启用 Swagger
if (app.Environment.IsDevelopment())
{
    app.UseSharedSwagger();
}
app.MapHealthChecks("/healthz");
app.MapPrometheusScrapingEndpoint();
app.MapHub<OrderBoardHub>("/hubs/order-board");
app.MapControllers();
app.Run();

// 13. 解析配置中的 CORS 域名
static string[] ResolveCorsOrigins(IConfiguration configuration, string sectionKey)
{
    var origins = configuration.GetSection(sectionKey).Get<string[]>();
    return origins?
        .Where(origin => !string.IsNullOrWhiteSpace(origin))
        .Distinct(StringComparer.OrdinalIgnoreCase)
        .ToArray() ?? [];
}

// 14. 构建 CORS 策略
static void ConfigureCorsPolicy(CorsPolicyBuilder policy, string[] origins)
{
    if (origins.Length == 0)
    {
        // SignalR 需要 AllowCredentials，与 AllowAnyOrigin 互斥，
        // 因此无配置时使用 SetIsOriginAllowed 替代。
        policy.SetIsOriginAllowed(_ => true)
            .AllowCredentials();
    }
    else
    {
        policy.WithOrigins(origins)
            .AllowCredentials();
    }

    policy
        .AllowAnyHeader()
        .AllowAnyMethod();
}
