import { mkdirSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";

import { afterAll, describe, expect, it, vi } from "vitest";

import { createApp } from "../../apps/api/src/app.js";
import {
  browserSupportFixture,
  testBrowserSupportRelease,
} from "../helpers/browser-support-fixture.js";

const supportedEdge = browserSupportFixture({
  brand: "Microsoft Edge",
  fullVersion: "150.0.4078.99",
});
const rejectedIdentityCases = [
  {
    expectedReason: "platform_unsupported",
    fixture: browserSupportFixture({
      brand: "Microsoft Edge",
      fullVersion: "150.0.4078.99",
      platform: "macOS",
    }),
    name: "non-Windows platform",
  },
  {
    expectedReason: "brand_unsupported",
    fixture: browserSupportFixture({ brand: "Opera", fullVersion: "150.0.0.0" }),
    name: "other brand",
  },
  {
    expectedReason: "version_unsupported",
    fixture: browserSupportFixture({ brand: "Microsoft Edge", fullVersion: "149.0.0.0" }),
    name: "wrong major",
  },
] as const;
const unavailableIdentityCases = [
  { mutate: () => ({ headers: { host: "127.0.0.1:43121" }, payload: supportedEdge.body }), name: "missing UA-CH" },
  {
    mutate: () => ({
      headers: { ...supportedEdge.headers, "sec-ch-ua-full-version-list": '"broken"' },
      payload: supportedEdge.body,
    }),
    name: "unparseable UA-CH",
  },
  {
    mutate: () => ({
      headers: supportedEdge.headers,
      payload: { ...supportedEdge.body, platform: "macOS" },
    }),
    name: "conflicting UA-CH",
  },
] as const;

function supportCookie(response: { headers: Record<string, string | string[] | undefined> }) {
  const header = response.headers["set-cookie"];
  const value = Array.isArray(header) ? header[0] : header;
  return value?.split(";", 1)[0];
}

afterAll(async () => {
  const evidenceDirectory = process.env.DADA_EVIDENCE_DIR_BRW_BLOCKED;
  if (!evidenceDirectory) return;

  const responses = [];
  const app = await createApp({ browserSupportRelease: testBrowserSupportRelease } as never);
  for (const testCase of rejectedIdentityCases) {
    const response = await app.inject({
      headers: testCase.fixture.headers,
      method: "POST",
      payload: testCase.fixture.body,
      url: "/api/v1/support/check",
    });
    responses.push({ body: response.json(), name: testCase.name, status_code: response.statusCode });
  }
  for (const testCase of unavailableIdentityCases) {
    const input = testCase.mutate();
    const response = await app.inject({
      headers: input.headers,
      method: "POST",
      payload: input.payload,
      url: "/api/v1/support/check",
    });
    responses.push({ body: response.json(), name: testCase.name, status_code: response.statusCode });
  }

  const bootstrap = vi.fn(() => ({
    app_version: "0.0.0",
    dependencies: [],
    model_summary: {
      config_set_version: null,
      configured_default_model_id: null,
      recommended_model_id: null,
      runtime_availability_version: null,
    },
    public_features: [],
  }));
  const rawApp = await createApp({ bootstrap, browserSupportRelease: testBrowserSupportRelease } as never);
  const rawResponse = await rawApp.inject({
    headers: { host: "127.0.0.1:43121", "user-agent": "raw-client" },
    method: "GET",
    url: "/api/v1/bootstrap",
  });
  responses.push({ body: rawResponse.json(), name: "raw product API", status_code: rawResponse.statusCode });
  await rawApp.close();
  await app.close();

  if (responses.some(({ status_code }) => status_code !== 426) || bootstrap.mock.calls.length !== 0) {
    throw new Error("Hard-block evidence did not preserve the 426 zero-business-access boundary.");
  }
  mkdirSync(evidenceDirectory, { recursive: true });
  writeFileSync(
    resolve(evidenceDirectory, "response.json"),
    `${JSON.stringify({ cases: responses, status: "passed" }, null, 2)}\n`,
  );
  writeFileSync(
    resolve(evidenceDirectory, "db-access.json"),
    `${JSON.stringify({ business_provider_calls: 0, reads: 0, status: "passed", writes: 0 }, null, 2)}\n`,
  );
  writeFileSync(
    resolve(evidenceDirectory, "external-calls.json"),
    `${JSON.stringify({ configured_clients: [], observed_calls: 0, status: "passed" }, null, 2)}\n`,
  );
});

describe("TDD-WP0-BRW-001 supported browser contract", () => {
  it("cross-checks UA-CH and issues only a short-lived signed support cookie", async () => {
    const app = await createApp({
      browserSupportRelease: testBrowserSupportRelease,
      productIndexHtml: "<!doctype html><title>Dada product test</title><div id=\"root\"></div>",
    } as never);
    const gate = await app.inject({ headers: { host: "127.0.0.1:43121" }, method: "GET", url: "/" });
    expect(gate.body).toContain("当前浏览器无法使用 Dada");
    const checked = await app.inject({
      headers: supportedEdge.headers,
      method: "POST",
      payload: supportedEdge.body,
      url: "/api/v1/support/check",
    });

    expect(checked.statusCode).toBe(200);
    expect(checked.json()).toEqual({
      app_version: "1.2.3-test",
      browser: { brand: "Microsoft Edge", major: 150 },
      status: "supported",
      supported_browsers: [
        { brand: "Google Chrome", major: 150 },
        { brand: "Microsoft Edge", major: 150 },
      ],
    });
    expect(checked.headers["set-cookie"]).toContain("dada_browser_support=");
    expect(checked.headers["set-cookie"]).toContain("HttpOnly");
    expect(checked.headers["set-cookie"]).toContain("SameSite=Strict");
    expect(checked.headers["set-cookie"]).toContain("Max-Age=86400");

    const cookie = supportCookie(checked);
    expect(cookie).toBeDefined();
    const productHtml = await app.inject({
      headers: { cookie, host: "127.0.0.1:43121", "sec-ch-ua": supportedEdge.headers["sec-ch-ua"] },
      method: "GET",
      url: "/app",
    });
    expect(productHtml.statusCode).toBe(200);
    expect(productHtml.body).toContain("Dada product test");
    expect(productHtml.body).not.toContain("当前浏览器无法使用 Dada");
    const product = await app.inject({
      headers: {
        cookie,
        host: "127.0.0.1:43121",
        "sec-ch-ua": supportedEdge.headers["sec-ch-ua"],
      },
      method: "GET",
      url: "/api/v1/bootstrap",
    });
    expect(product.statusCode).toBe(200);
    await app.close();

    const restarted = await createApp({ browserSupportRelease: testBrowserSupportRelease } as never);
    const staleCookie = await restarted.inject({
      headers: {
        cookie,
        host: "127.0.0.1:43121",
        "sec-ch-ua": supportedEdge.headers["sec-ch-ua"],
      },
      method: "GET",
      url: "/api/v1/bootstrap",
    });
    expect(staleCookie.statusCode).toBe(426);
    await restarted.close();
  });
});

describe("TDD-WP0-BRW-002 hard block", () => {
  it.each(rejectedIdentityCases)("returns 426 for $name", async ({ expectedReason, fixture }) => {
    const app = await createApp({ browserSupportRelease: testBrowserSupportRelease } as never);
    const response = await app.inject({
      headers: fixture.headers,
      method: "POST",
      payload: fixture.body,
      url: "/api/v1/support/check",
    });

    expect(response.statusCode).toBe(426);
    expect(response.json()).toMatchObject({
      error: {
        code: "BROWSER_UNSUPPORTED",
        details: {
          reason: expectedReason,
          supported_browsers: [
            { brand: "Google Chrome", major: 150 },
            { brand: "Microsoft Edge", major: 150 },
          ],
        },
        message_key: "browser.unsupported",
      },
    });
    expect(response.body).not.toContain("Opera");
    await app.close();
  });

  it.each(unavailableIdentityCases)("fails closed for $name", async ({ mutate }) => {
    const app = await createApp({ browserSupportRelease: testBrowserSupportRelease } as never);
    const input = mutate();
    const response = await app.inject({
      headers: input.headers,
      method: "POST",
      payload: input.payload,
      url: "/api/v1/support/check",
    });

    expect(response.statusCode).toBe(426);
    expect(response.json()).toMatchObject({
      error: { details: { reason: "identity_unavailable" } },
    });
    await app.close();
  });

  it("blocks raw product API access before any business provider runs", async () => {
    const bootstrap = vi.fn(() => ({
      app_version: "0.0.0",
      dependencies: [],
      model_summary: {
        config_set_version: null,
        configured_default_model_id: null,
        recommended_model_id: null,
        runtime_availability_version: null,
      },
      public_features: [],
    }));
    const app = await createApp({
      bootstrap,
      browserSupportRelease: testBrowserSupportRelease,
    } as never);
    const response = await app.inject({
      headers: { host: "127.0.0.1:43121", "user-agent": "Mozilla/5.0 Chrome/150" },
      method: "GET",
      url: "/api/v1/bootstrap",
    });

    expect(response.statusCode).toBe(426);
    expect(response.json()).toMatchObject({
      error: {
        code: "BROWSER_UNSUPPORTED",
        details: { reason: "identity_unavailable" },
      },
    });
    expect(bootstrap).not.toHaveBeenCalled();
    await app.close();
  });
});
