using System.Net;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;

namespace Flyshot.Server.IntegrationTests;

/// <summary>
/// 验证 `wwwroot` 静态调试页和调试配置 API 的基础内容契约。
/// </summary>
/// <remarks>
/// 调试页自身是静态 HTML，真正的 Swagger 地址由配置 API 下发；
/// 当 Swagger 关闭时，配置 API 返回 404，前端据此显示不可用状态。
/// </remarks>
public sealed class DebugConsoleEndpointTests(FlyshotServerFactory factory) : IClassFixture<FlyshotServerFactory>
{
    private readonly FlyshotServerFactory _factory = factory;

    /// <summary>
    /// `debug.html` 应当作为可直接调试的静态页面暴露。
    /// </summary>
    [Fact]
    public async Task GetDebugHtml_ReturnsConsoleStaticPage()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(true);
        using var client = configuredFactory.CreateClient();

        using var response = await client.GetAsync("/debug.html");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.StartsWith("text/html", response.Content.Headers.ContentType?.MediaType);

        var html = await response.Content.ReadAsStringAsync();

        Assert.Contains("Flyshot Replacement 接口调试", html, StringComparison.Ordinal);
        Assert.Contains("id=\"debug-console-app\"", html, StringComparison.Ordinal);
        Assert.DoesNotContain("__SWAGGER_JSON_URL__", html, StringComparison.Ordinal);
        Assert.Contains("/assets/debug.css", html, StringComparison.Ordinal);
        Assert.Contains("/assets/debug.js", html, StringComparison.Ordinal);

        using var scriptResponse = await client.GetAsync("/assets/debug.js");
        Assert.Equal(HttpStatusCode.OK, scriptResponse.StatusCode);

        var script = await scriptResponse.Content.ReadAsStringAsync();
        Assert.Contains("/api/debug/config", script, StringComparison.Ordinal);
    }

    /// <summary>
    /// 调试页应当为 MovePose 提供可直接发送的六字段请求体模板。
    /// </summary>
    [Fact]
    public async Task GetDebugScript_ContainsMovePoseRequestSample()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(true);
        using var client = configuredFactory.CreateClient();

        using var scriptResponse = await client.GetAsync("/assets/debug.js");
        Assert.Equal(HttpStatusCode.OK, scriptResponse.StatusCode);

        var script = await scriptResponse.Content.ReadAsStringAsync();
        Assert.Contains("/move_pose/", script, StringComparison.Ordinal);
        Assert.Contains("x: 100.0", script, StringComparison.Ordinal);
        Assert.Contains("y: 200.0", script, StringComparison.Ordinal);
        Assert.Contains("z: 300.0", script, StringComparison.Ordinal);
        Assert.Contains("w: 0.0", script, StringComparison.Ordinal);
        Assert.Contains("p: 45.0", script, StringComparison.Ordinal);
        Assert.Contains("r: 0.0", script, StringComparison.Ordinal);
    }

    /// <summary>
    /// 当 Swagger 启用时，调试配置 API 应当返回实际 Swagger JSON 地址。
    /// </summary>
    [Fact]
    public async Task GetDebugConfig_WhenSwaggerEnabled_ReturnsSwaggerJsonUrl()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(true);
        using var client = configuredFactory.CreateClient();

        using var response = await client.GetAsync("/api/debug/config");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        await using var responseStream = await response.Content.ReadAsStreamAsync();
        using var document = await System.Text.Json.JsonDocument.ParseAsync(responseStream);

        Assert.Equal("/swagger/v1/swagger.json", document.RootElement.GetProperty("swaggerJsonUrl").GetString());
    }

    /// <summary>
    /// 当 Swagger 关闭时，调试配置 API 应当与 Swagger UI 同步下线（404）。
    /// </summary>
    [Fact]
    public async Task GetDebugConfig_WhenSwaggerDisabled_ReturnsNotFound()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(false);
        using var client = configuredFactory.CreateClient();

        using var response = await client.GetAsync("/api/debug/config");

        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    /// <summary>
    /// 调试页需要从 Swagger JSON 中读取所有端点，因此 Swagger 文档必须包含基础和兼容层的代表性路由。
    /// </summary>
    [Fact]
    public async Task SwaggerDocument_ContainsRepresentativeRoutesForDebugConsole()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(true);
        using var client = configuredFactory.CreateClient();

        using var response = await client.GetAsync("/swagger/v1/swagger.json");
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        await using var responseStream = await response.Content.ReadAsStreamAsync();
        using var document = await System.Text.Json.JsonDocument.ParseAsync(responseStream);
        var paths = document.RootElement.GetProperty("paths");

        // 这些路径分别覆盖：基础探活、状态快照、版本查询、上传飞拍轨迹四种典型形态。
        AssertPathExists(paths, "/healthz");
        AssertPathExists(paths, "/api/status/snapshot");
        AssertPathExists(paths, "/get_server_version");
        AssertPathExists(paths, "/upload_flyshot");
    }

    /// <summary>
    /// 状态页应当提供跳转到静态调试页的入口，便于现场顺手跳转。
    /// </summary>
    [Fact]
    public async Task GetStatusHtml_LinksToDebugConsole()
    {
        using var configuredFactory = CreateFactoryWithSwaggerEnabled(true);
        using var client = configuredFactory.CreateClient();

        using var response = await client.GetAsync("/status.html");

        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        var html = await response.Content.ReadAsStringAsync();

        Assert.Contains("href=\"/debug.html\"", html, StringComparison.Ordinal);
    }

    /// <summary>
    /// 检查 Swagger 文档中是否存在指定路径，兼容尾斜杠归一化两种形态。
    /// </summary>
    /// <param name="paths">OpenAPI 文档中的 paths 节点。</param>
    /// <param name="route">期望存在的路由字符串。</param>
    private static void AssertPathExists(System.Text.Json.JsonElement paths, string route)
    {
        // OpenAPI 生成器会把部分尾斜杠路径规范化，这里同时接受两种形态。
        var withSlash = route.EndsWith('/') ? route : route + "/";
        var withoutSlash = route.EndsWith('/') ? route.TrimEnd('/') : route;

        Assert.True(
            paths.TryGetProperty(withSlash, out _) || paths.TryGetProperty(withoutSlash, out _),
            $"Swagger 文档应当包含路径 {route}");
    }

    /// <summary>
    /// 构造覆盖了 <c>Swagger:Enabled</c> 配置项的测试宿主工厂。
    /// </summary>
    /// <param name="enabled">期望的 Swagger 启用状态。</param>
    /// <returns>已应用配置覆盖的测试工厂。</returns>
    private WebApplicationFactory<Program> CreateFactoryWithSwaggerEnabled(bool enabled)
    {
        return _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureAppConfiguration((_, configurationBuilder) =>
            {
                // 通过 InMemory 配置覆盖 appsettings.json 中的 Swagger 开关，避免修改磁盘文件。
                configurationBuilder.AddInMemoryCollection(new Dictionary<string, string?>
                {
                    ["Swagger:Enabled"] = enabled ? "true" : "false",
                    ["Swagger:DocumentName"] = "v1",
                    ["Swagger:Title"] = "Flyshot Replacement HTTP API",
                    ["Swagger:Version"] = "v1",
                    ["Swagger:JsonRouteTemplate"] = "swagger/{documentName}/swagger.json",
                    ["Swagger:RoutePrefix"] = "swagger"
                });
            });
        });
    }
}
