/**
 * 相関IDの取得規則（ステップ9 の共通ヘルパー）。
 * proxy.ts（ステップ7）が保留中でも request_id が欠けないことを固定する。
 */
import { describe, expect, test } from "vitest";
import {
  getRequestId,
  normalizeRequestId,
  REQUEST_ID_HEADER,
  runWithRequestId,
  withRequestId,
} from "@/lib/logger/context";

function requestWith(headers: Record<string, string> = {}): Request {
  return new Request("http://localhost/api/clean-logs", { headers });
}

describe("withRequestId", () => {
  test("x-request-id ヘッダーがあればそれを使う", () => {
    const request = requestWith({ [REQUEST_ID_HEADER]: "from-proxy-0001" });
    const seen = withRequestId(request, () => getRequestId());
    expect(seen).toBe("from-proxy-0001");
  });

  test("ヘッダーが無ければ生成する（UUID 形式）", () => {
    const seen = withRequestId(requestWith(), () => getRequestId());
    expect(seen).toMatch(
      /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
    );
  });

  test("リクエストごとに別のIDになる", () => {
    const a = withRequestId(requestWith(), () => getRequestId());
    const b = withRequestId(requestWith(), () => getRequestId());
    expect(a).not.toBe(b);
  });

  test("形が不正なヘッダー値は使わずに生成する（改行・長すぎる値）", () => {
    const injected = withRequestId(
      requestWith({ [REQUEST_ID_HEADER]: "abc def" }),
      () => getRequestId()
    );
    expect(injected).not.toBe("abc def");

    const tooLong = withRequestId(
      requestWith({ [REQUEST_ID_HEADER]: "a".repeat(129) }),
      () => getRequestId()
    );
    expect(tooLong).not.toBe("a".repeat(129));
  });

  test("空文字のヘッダー値は生成にフォールバックする", () => {
    const seen = withRequestId(
      requestWith({ [REQUEST_ID_HEADER]: "" }),
      () => getRequestId()
    );
    expect(seen).toBeTruthy();
    expect(seen).not.toBe("");
  });

  test("非同期の内側でも同じIDが読める", async () => {
    const request = requestWith({ [REQUEST_ID_HEADER]: "async-0002" });
    const seen = await withRequestId(request, async () => {
      await new Promise((resolve) => setTimeout(resolve, 1));
      return getRequestId();
    });
    expect(seen).toBe("async-0002");
  });

  test("ラップの外ではIDが残らない", () => {
    withRequestId(requestWith(), () => getRequestId());
    expect(getRequestId()).toBeUndefined();
  });

  test("runWithRequestId は明示指定した値をそのまま使う", () => {
    expect(runWithRequestId("explicit-0003", () => getRequestId())).toBe(
      "explicit-0003"
    );
  });
});

/**
 * onRequestError() はルートの AsyncLocalStorage の外で走るため、
 * ヘッダから同じ規則でIDを取り直す。その規則をここで固定する。
 */
describe("normalizeRequestId", () => {
  test("妥当な値はそのまま返す", () => {
    expect(normalizeRequestId("from-proxy-0001")).toBe("from-proxy-0001");
  });

  test("Node 形式の配列ヘッダは先頭を見る", () => {
    expect(normalizeRequestId(["first-0001", "second-0002"])).toBe(
      "first-0001"
    );
  });

  test("未設定・空・不正な形は undefined（偽の相関を作らない）", () => {
    expect(normalizeRequestId(undefined)).toBeUndefined();
    expect(normalizeRequestId(null)).toBeUndefined();
    expect(normalizeRequestId("")).toBeUndefined();
    expect(normalizeRequestId("has space")).toBeUndefined();
    expect(normalizeRequestId("line\nbreak")).toBeUndefined();
    expect(normalizeRequestId("a".repeat(129))).toBeUndefined();
  });
});
