using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.OpenApi.Models;
using MiGu.Server.Auth;
using MiGu.Server.Configs;
using MiGu.Server.Launcher;
using Yarp.ReverseProxy.Transforms;

var builder = WebApplication.CreateBuilder(args);

// S2 根治 (会话45)：让 MiGu.Server 启动时自动优先把 frontends/apps/simple-platform-vue/dist/
// 作为 WebRoot —— 解决「每次 vite build → wwwroot/index.html 都被 hash 漂移 → 提一个噪音 commit」
// 的死循环。
//
// 探测策略：从 ContentRootPath 出发向上逐级找 frontends/apps/simple-platform-vue/dist/index.html,
// 找到则将 WebRootPath 改指到 dist；找不到则保持默认 wwwroot/（用于「克隆后没跑 build」或
// 生产部署机不带 node 的场景，wwwroot/ 由 build-platform-frontend.bat 的 robocopy /MIR 兜底）。
//
// 影响：
// - dev 模式：跑过一次 `pnpm build` 后无需 robocopy，重启 MiGu.Server 即生效；
//   也不再需要 commit wwwroot/index.html。
// - 部署：CI/CD 走 build-platform-frontend.bat 把 dist 同步到 wwwroot/，dist 此时不存在
//   于发布产物中 → 自动 fallback 到 wwwroot/。行为与改造前一致。
{
    static string? FindDistRoot(string startDir)
    {
        var dir = new DirectoryInfo(startDir);
        while (dir != null)
        {
            var candidate = Path.Combine(dir.FullName, "frontends", "apps", "simple-platform-vue", "dist", "index.html");
            if (File.Exists(candidate)) return Path.GetDirectoryName(candidate);
            dir = dir.Parent;
        }
        return null;
    }

    var dist = FindDistRoot(builder.Environment.ContentRootPath);
    if (dist != null)
    {
        builder.Environment.WebRootPath = dist;
        Console.WriteLine($"[MiGu.Server] WebRoot -> dist: {dist}");
    }
    else
    {
        Console.WriteLine($"[MiGu.Server] WebRoot -> wwwroot (dist not found): {builder.Environment.WebRootPath}");
    }
}

// 控制器 + JSON 默认大小写、忽略 null
builder.Services.AddControllers()
    .AddJsonOptions(opt =>
    {
        opt.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
        opt.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
        opt.JsonSerializerOptions.WriteIndented = false;
    });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new() { Title = "MiGu.Server", Version = "v1", Description = "Simple-FR 平台后端骨架（含 YARP 反代 SimpleLite 8222）。" });
    // Swagger 里挂 Bearer 输入框，便于手工测带鉴权的端点。
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Description = "JWT bearer。值: \"Bearer {token}\"",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
            },
            Array.Empty<string>()
        }
    });
});

// CORS（Vite dev 5173 与 MiGu.Server 8080 跨域）
var origins = builder.Configuration.GetSection("Cors:Origins").Get<string[]>() ?? Array.Empty<string>();
builder.Services.AddCors(opts => opts.AddDefaultPolicy(p =>
    p.WithOrigins(origins).AllowAnyHeader().AllowAnyMethod().AllowCredentials()));

// ─── AR-3 / AR-5：JWT + Cookie 双轨鉴权 ─────────────────────────────────────────
//
// 设计：
// - access token 同时通过 Authorization: Bearer header 与 httpOnly Cookie 两路传输；
// - 旧 SPA 还在用 localStorage.token + Bearer，逐步迁移到 Cookie；过渡期两路都接受；
// - JwtIssuer 集中颁发 + 验签；secret 来自 appsettings:Jwt:Secret 或环境变量
//   PLATFORM__JWT__SECRET，占位值会被运行时随机化并强制告警。
// - InternalTokenStore 管理 SimpleLite 8222 ↔ MiGu.Server 之间的 X-Platform-Internal-Token
//   共享密钥（YARP transform 自动追加）。
builder.Services.AddSingleton<RbacStore>();
builder.Services.AddSingleton<JwtIssuer>(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    var logger = sp.GetRequiredService<ILogger<JwtIssuer>>();
    var secret = config["Jwt:Secret"] ?? JwtIssuer.PlaceholderSecret;
    var issuer = config["Jwt:Issuer"] ?? "MiGu.Server";
    var audience = config["Jwt:Audience"] ?? "platform.client";
    var lifetimeMinutes = int.TryParse(config["Jwt:LifetimeMinutes"], out var m) && m > 0 ? m : 24 * 60;
    return new JwtIssuer(secret, issuer, audience, TimeSpan.FromMinutes(lifetimeMinutes), logger);
});
builder.Services.AddSingleton<InternalTokenStore>();

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(opt =>
    {
        // TokenValidationParameters 在第一次解析请求时从 JwtIssuer 拿，避免 ctor 顺序耦合。
        opt.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            // 完整参数在 OnMessageReceived 里替换为 JwtIssuer.BuildValidationParameters()
            ValidateIssuer = false,
            ValidateAudience = false,
            ValidateIssuerSigningKey = false,
            ValidateLifetime = false,
        };
        opt.Events = new JwtBearerEvents
        {
            OnMessageReceived = ctx =>
            {
                // 优先从 Authorization: Bearer 取；没有再从 Cookie 取。
                if (string.IsNullOrEmpty(ctx.Token))
                {
                    var cookie = ctx.Request.Cookies["simple.auth.token"];
                    if (!string.IsNullOrEmpty(cookie)) ctx.Token = cookie;
                }
                // 用真实 JwtIssuer 参数替换占位 ValidationParameters。
                var issuer = ctx.HttpContext.RequestServices.GetRequiredService<JwtIssuer>();
                ctx.Options.TokenValidationParameters = issuer.BuildValidationParameters();
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddAuthorization(opts =>
{
    // Platform scope：完整管理端权限，对应 admin 账号。
    opts.AddPolicy("PlatformScope", p => p.RequireAuthenticatedUser().RequireClaim("scope", "Platform"));
    // RCSMonitor scope：运营白名单。
    opts.AddPolicy("MonitorScope", p => p.RequireAuthenticatedUser().RequireClaim("scope", "RCSMonitor"));
    // 任一登录用户。
    opts.AddPolicy("AnyAuthed", p => p.RequireAuthenticatedUser());
    // RBAC 管理：JWT 的 ops claim（空格分隔）含 "*" 或 "auth.manage" 才放行。
    // 用于 RbacController（用户 / 角色 / 权限页面管理），即「超级管理员」类账号专属。
    opts.AddPolicy("RbacAdmin", p => p.RequireAuthenticatedUser().RequireAssertion(ctx =>
    {
        var ops = ctx.User.FindFirst("ops")?.Value ?? string.Empty;
        var set = ops.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return set.Contains("*") || set.Contains("auth.manage");
    }));
});

// YARP + transform：把 Platform 内部 token 透传给 SimpleLite 8222（AR-1/AR-2 配套）。
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddTransforms(tctx =>
    {
        // 只对 sl-route 注入 internal token；vrender-route（webVRender iframe 静态资源）不需要。
        if (tctx.Route.RouteId != "sl-route") return;
        tctx.AddRequestTransform(rt =>
        {
            var store = rt.HttpContext.RequestServices.GetRequiredService<InternalTokenStore>();
            rt.ProxyRequest.Headers.Remove("X-Platform-Internal-Token");
            rt.ProxyRequest.Headers.Add("X-Platform-Internal-Token", store.Token);
            return ValueTask.CompletedTask;
        });
    });

// 单例配置仓库（内存 + JSON 文件持久化占位）
builder.Services.AddSingleton<ConfigStore>();

// 会话 N+1（启动反转）：把 SimpleLite 子进程拉起器接入 DI；AuthController 登录成功后按 LaunchMode 调 MaybeStart。
// 配置段绑定 appsettings.json:SimpleLite，可被环境变量 SIMPLELITE__XXX 覆盖。
builder.Services.Configure<SimpleLiteOptions>(builder.Configuration.GetSection("SimpleLite"));
builder.Services.AddSingleton<SimpleLiteLauncher>();

var app = builder.Build();

// 启动期主动构造 JwtIssuer / InternalTokenStore：让 secret 校验日志在请求来之前打印。
_ = app.Services.GetRequiredService<JwtIssuer>();
_ = app.Services.GetRequiredService<InternalTokenStore>();
// 主动构造 RbacStore：首启时尽早 seed 默认用户 / 角色并打印 data/rbac.json 载入日志。
_ = app.Services.GetRequiredService<RbacStore>();
// 主动实例化 SimpleLiteLauncher（FollowParent=true 时注册 ProcessExit 软关闭钩子）。
var simpleLiteLauncher = app.Services.GetRequiredService<SimpleLiteLauncher>();
{
    var sl = simpleLiteLauncher.GetDiagnostics();
    app.Logger.LogInformation(
        "[MiGu.Server] SimpleLite: Enabled={Enabled}, FollowParent={FollowParent}, ConfiguredPath={Cfg}, Resolved={Resolved}, Exists={Exists}, Port:{Port} reachable={PortUp}. 配置见 appsettings.json → SimpleLite",
        sl.Enabled, sl.FollowParent, sl.ConfiguredExecutablePath, sl.ResolvedExecutablePath ?? "(未找到)", sl.ExecutableExists,
        sl.ProjectionPort, sl.ProjectionPortReachable);
}

// FollowParent=true 时 MiGu.Server 退出会 kill SimpleLite；默认 false 时不注册停机清理（两进程独立）。
if (builder.Configuration.GetValue("SimpleLite:FollowParent", false))
{
    app.Lifetime.ApplicationStopping.Register(() =>
    {
        try { app.Services.GetRequiredService<SimpleLiteLauncher>().Dispose(); }
        catch { /* shutdown best-effort */ }
    });
}
else
{
    app.Logger.LogInformation("[MiGu.Server] SimpleLite: FollowParent=false — MiGu.Server 退出不会结束 SimpleLite");
}

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseCors();

// 静态资源：MiGu.Server/wwwroot 下可同时存放 admin / monitor / index 三份 SPA
app.UseDefaultFiles();
app.UseStaticFiles();

// 鉴权 / 授权管道必须放在 MapControllers 之前；CORS 之后。
app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

// YARP：/api/sl/* → :8222；/vr/* → :8223
app.MapReverseProxy();

// SPA fallback：单一合并 Vue 工程（vue-router 处理 /admin/* /monitor/* /login /status）
// 所有非 API、非静态资源的路径都返回 wwwroot/index.html
app.MapFallbackToFile("index.html");

app.Run();
