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

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

import { createApp } from "../../apps/api/src/app.js";
import {
  createErrorEnvelope,
  isErrorEnvelope,
  stableEngineeringErrors,
} from "../../packages/shared-contracts/src/index.js";

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

  const app = await createApp({ browserGate: false });
  await app.ready();
  const document = app.swagger();
  const response = await app.inject({
    headers: {
      host: "127.0.0.1:43121",
      "x-correlation-id": "018f6d52-3348-7a3a-a741-7f65f31f9218",
    },
    method: "GET",
    url: "/api/v1/bootstrap",
  });
  await app.close();

  const responseEvidence = {
    body: response.json(),
    headers: { "x-correlation-id": response.headers["x-correlation-id"] },
    status_code: response.statusCode,
  };
  const serialized = JSON.stringify(responseEvidence);
  const findings = [
    /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
    /[A-Za-z]:\\Users\\[^\\\s]+/,
    /(?:api[_-]?key|password|secret)\s*[:=]\s*["'][^"']{8,}["']/i,
  ].filter((pattern) => pattern.test(serialized)).map((pattern) => pattern.source);
  expect(findings).toEqual([]);

  mkdirSync(evidenceDirectory, { recursive: true });
  writeFileSync(resolve(evidenceDirectory, "openapi.json"), `${JSON.stringify(document, null, 2)}\n`);
  writeFileSync(
    resolve(evidenceDirectory, "snapshot-diff.json"),
    `${JSON.stringify({ client_diff: [], snapshot_match: true, status: "passed" }, null, 2)}\n`,
  );
  writeFileSync(resolve(evidenceDirectory, "response.json"), `${JSON.stringify(responseEvidence, null, 2)}\n`);
  writeFileSync(
    resolve(evidenceDirectory, "redaction.json"),
    `${JSON.stringify({ findings, status: "passed" }, null, 2)}\n`,
  );
});

describe("TDD-WP0-API-001 schema envelope", () => {
  it("generates the committed OpenAPI 3.1 snapshot from runtime schemas", async () => {
    const app = await createApp({ browserGate: false });
    await app.ready();

    const document = app.swagger();
    const snapshot = JSON.parse(readFileSync("openapi/openapi.json", "utf8"));

    expect(document.openapi).toBe("3.1.0");
    expect(document).toEqual(snapshot);
    expect(document.paths).toHaveProperty("/api/v1/bootstrap");
    expect(document.paths).toHaveProperty("/api/v1/events");
    expect(document.components?.schemas).toHaveProperty("ErrorEnvelope");
    expect(document.components?.schemas).toHaveProperty("SseEvent");

    await app.close();
  });

  it("keeps the generated frontend SDK aligned with the OpenAPI operations", () => {
    const sdk = readFileSync("apps/web/src/generated/api/sdk.gen.ts", "utf8");
    const types = readFileSync("apps/web/src/generated/api/types.gen.ts", "utf8");

    expect(sdk).toContain("getBootstrap");
    expect(sdk).toContain("getEvents");
    expect(types).toContain("ErrorEnvelope");
    expect(types).toContain("SseEvent");
  });

  it("echoes a valid correlation UUID and replaces an invalid one", async () => {
    const app = await createApp({ browserGate: false });
    await app.ready();

    const accepted = await app.inject({
      headers: {
        host: "127.0.0.1:43121",
        "x-correlation-id": "018f6d52-3348-7a3a-a741-7f65f31f9218",
      },
      method: "GET",
      url: "/api/v1/bootstrap",
    });
    const replaced = await app.inject({
      headers: { host: "127.0.0.1:43121", "x-correlation-id": "not-a-uuid" },
      method: "GET",
      url: "/api/v1/bootstrap",
    });

    expect(accepted.headers["x-correlation-id"]).toBe("018f6d52-3348-7a3a-a741-7f65f31f9218");
    expect(replaced.headers["x-correlation-id"]).toMatch(
      /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
    );
    expect(replaced.headers["x-correlation-id"]).not.toBe("not-a-uuid");

    await app.close();
  });

  it("accepts only the frozen error codes and details whitelist", () => {
    expect(Object.keys(stableEngineeringErrors).sort()).toEqual([
      "ASSET_CLEANUP_CANDIDATE_STALE",
      "ASSET_HISTORY_REFERENCE_CONFLICT",
      "AUTH_CSRF_INVALID",
      "AUTH_ENTRY_REJECTED",
      "AUTH_RATE_LIMITED",
      "AUTH_SERVICE_UNAVAILABLE",
      "AUTH_SESSION_INVALID",
      "BROWSER_UNSUPPORTED",
      "IDEMPOTENCY_KEY_CONFLICT",
      "MODEL_CONFIG_VERSION_CONFLICT",
      "MODEL_DEFAULT_REPLACEMENT_INVALID",
      "MODEL_DEFAULT_REPLACEMENT_REQUIRED",
      "MODEL_RECOMMENDATION_PRIORITY_CONFLICT",
      "MODEL_RECOMMENDATION_PRIORITY_INVALID",
      "PRIVATE_CONTENT_NOTICE_ACK_REQUIRED",
      "REGISTRATION_REJECTED",
      "REGISTRATION_REQUEST_INVALID",
      "STORAGE_CAPACITY_EXCEEDED",
    ]);

    const envelope = createErrorEnvelope({
      code: "MODEL_CONFIG_VERSION_CONFLICT",
      correlationId: "018f6d52-3348-7a3a-a741-7f65f31f9218",
      details: { latest_version: 4 },
    });
    expect(isErrorEnvelope(envelope)).toBe(true);
    expect(envelope.error.message_key).toBe("MODEL_CONFIG_VERSION_CONFLICT");

    const leaked = {
      error: {
        ...envelope.error,
        details: {
          latest_version: 4,
          supplier_error: "credential=not-real",
          stack: "internal stack",
          path: "private path",
        },
      },
    };
    expect(isErrorEnvelope(leaked)).toBe(false);
  });
});
