import { describe, expect, it } from "vitest";
import {
  LANG_STORAGE_KEY,
  PREFS_INLINE_SCRIPT,
  THEME_STORAGE_KEY,
  parseSavedLang,
  parseSavedTheme,
} from "@/lib/ui-prefs";
import type { Lang } from "@/lib/i18n";
import type { ThemeName } from "@/lib/theme";

/**
 * 表示設定（ダーク/ライト・JP/EN）の保存（ユーザー決定 2026-10-19 U1。指示書BC 段階3。D-183）。
 * ★許す値は "dark" / "light" と "ja" / "en" だけ★。それ以外は null（既定で描く）。
 */
describe("parseSavedTheme — 保存値をテーマとして読む", () => {
  const cases: Array<[string, string | null, ThemeName | null]> = [
    ["T1 dark", "dark", "dark"],
    ["T2 light", "light", "light"],
    ["T3 null（保存なし）", null, null],
    ["T4 大文字違いは許さない", "Light", null],
    ["T5 空文字", "", null],
  ];

  it.each(cases)("%s", (_name, input, expected) => {
    expect(parseSavedTheme(input)).toBe(expected);
  });
});

describe("parseSavedLang — 保存値を言語として読む", () => {
  const cases: Array<[string, string | null, Lang | null]> = [
    ["L1 ja", "ja", "ja"],
    ["L2 en", "en", "en"],
    ["L3 null（保存なし）", null, null],
    ["L4 jp（違う綴り）", "jp", null],
  ];

  it.each(cases)("%s", (_name, input, expected) => {
    expect(parseSavedLang(input)).toBe(expected);
  });
});

/**
 * `<head>` で描く前に動かすスクリプト。★`vitest.config.ts` の環境は node なので DOM は使わず★、
 * 偽の `document`（`documentElement.setAttribute` を Map に記録する）と偽の `localStorage` を渡して呼ぶ。
 * 偽の `<html>` は最初 `data-theme=dark`・`lang=ja`（`app/layout.tsx` の既定と同じ）。
 */
describe("PREFS_INLINE_SCRIPT — 描く前に <html> の属性を保存値に合わせる", () => {
  function run(getItem: (key: string) => string | null): Map<string, string> {
    const attrs = new Map<string, string>([
      ["data-theme", "dark"],
      ["lang", "ja"],
    ]);
    const fakeDocument = {
      documentElement: {
        setAttribute: (name: string, value: string) => {
          attrs.set(name, value);
        },
      },
    };
    const fakeStorage = { getItem };
    const script = new Function("document", "localStorage", PREFS_INLINE_SCRIPT);
    script(fakeDocument, fakeStorage);
    return attrs;
  }

  function storageOf(values: Record<string, string>): (key: string) => string | null {
    return (key) => (key in values ? values[key] : null);
  }

  it("S1 保存 light / en → data-theme=light・lang=en", () => {
    const attrs = run(storageOf({ [THEME_STORAGE_KEY]: "light", [LANG_STORAGE_KEY]: "en" }));
    expect(attrs.get("data-theme")).toBe("light");
    expect(attrs.get("lang")).toBe("en");
  });

  it("S2 許さない値（blue / fr）→ 変えない（dark / ja のまま）", () => {
    const attrs = run(storageOf({ [THEME_STORAGE_KEY]: "blue", [LANG_STORAGE_KEY]: "fr" }));
    expect(attrs.get("data-theme")).toBe("dark");
    expect(attrs.get("lang")).toBe("ja");
  });

  it("S3 getItem が例外を投げる → 例外が外に出ない・変えない", () => {
    // 例外が外に出たら attrs は空のまま（下の2行も落ちる）
    let attrs = new Map<string, string>();
    expect(() => {
      attrs = run(() => {
        throw new Error("SecurityError");
      });
    }).not.toThrow();
    expect(attrs.get("data-theme")).toBe("dark");
    expect(attrs.get("lang")).toBe("ja");
  });

  it("S4 何も保存されていない → 変えない", () => {
    const attrs = run(() => null);
    expect(attrs.get("data-theme")).toBe("dark");
    expect(attrs.get("lang")).toBe("ja");
  });
});
