import { Alepha } from "alepha";
import { FileSystemProvider, MemoryFileSystemProvider } from "alepha/system";
import { beforeEach, describe, expect, it } from "vitest";
import { I18nCheckService } from "../services/I18nCheckService.ts";

describe("I18nCheckService", () => {
  const ROOT = "/proj";

  const createEnv = () => {
    const alepha = Alepha.create().with({
      provide: FileSystemProvider,
      use: MemoryFileSystemProvider,
    });
    const service = alepha.inject(I18nCheckService);
    const fs = alepha.inject(MemoryFileSystemProvider);
    return { service, fs };
  };

  const dictionary = (keys: Record<string, string>) => {
    const body = Object.entries(keys)
      .map(([k, v]) => `        "${k}": "${v}",`)
      .join("\n");
    return `import { $dictionary } from "alepha/react/i18n";
export class I18n {
  en = $dictionary({
    lazy: async () => ({
      default: {
${body}
      },
    }),
  });
}
`;
  };

  let env: ReturnType<typeof createEnv>;
  beforeEach(() => {
    env = createEnv();
  });

  it("reports keys with no quoted reference as unused", async () => {
    await env.fs.mkdir(`${ROOT}/src/web`, { recursive: true });
    await env.fs.writeFile(
      `${ROOT}/src/web/I18n.ts`,
      dictionary({ "home.title": "Home", "home.unused": "Nothing" }),
    );
    await env.fs.writeFile(
      `${ROOT}/src/web/Home.tsx`,
      `export const Home = () => tr("home.title");`,
    );

    const result = await env.service.check({
      root: ROOT,
      scan: ["src"],
      dynamicPrefixes: [],
      exclude: [],
    });

    expect(result.totalKeys).toBe(2);
    expect(result.unused).toEqual(["home.unused"]);
    expect(result.dictionaryFiles).toHaveLength(1);
  });

  it("exempts keys matching a dynamic prefix", async () => {
    await env.fs.mkdir(`${ROOT}/src`, { recursive: true });
    await env.fs.writeFile(
      `${ROOT}/src/I18n.ts`,
      dictionary({
        "archive.type.directory": "Folder",
        "archive.type.folio": "Folio",
        "home.title": "Home",
      }),
    );
    await env.fs.writeFile(
      `${ROOT}/src/App.tsx`,
      "tr(`archive.type.${k}`); tr('home.title');",
    );

    const result = await env.service.check({
      root: ROOT,
      scan: ["src"],
      dynamicPrefixes: ["archive.type."],
      exclude: [],
    });

    expect(result.unused).toEqual([]);
    expect(result.exemptKeys).toBe(2);
  });

  it("skips excluded paths and built-in excludes", async () => {
    await env.fs.mkdir(`${ROOT}/src/__tests__`, { recursive: true });
    await env.fs.writeFile(
      `${ROOT}/src/I18n.ts`,
      dictionary({ "home.title": "Home" }),
    );
    // Reference lives only in an excluded test file — should not count.
    await env.fs.writeFile(
      `${ROOT}/src/__tests__/Home.spec.ts`,
      `tr("home.title");`,
    );

    const result = await env.service.check({
      root: ROOT,
      scan: ["src"],
      dynamicPrefixes: [],
      exclude: [],
    });

    expect(result.unused).toEqual(["home.title"]);
  });

  it("extracts keys from lazily-imported per-language files", async () => {
    await env.fs.mkdir(`${ROOT}/src/web/i18n`, { recursive: true });
    // Marker file declares `$dictionary` but the keys live in split,
    // markerless per-language files referenced via `lazy: () => import(...)`.
    // A sibling `$page` lazy import must NOT be mistaken for a key file.
    await env.fs.writeFile(
      `${ROOT}/src/web/Router.ts`,
      `import { $dictionary } from "alepha/react/i18n";
export class Router {
  fr = $dictionary({ lazy: () => import("./i18n/fr.ts"), lang: "fr" });
  en = $dictionary({ lazy: () => import("./i18n/en.ts"), lang: "en" });
  page = $page({ lazy: () => import("./Home.tsx") });
}
`,
    );
    await env.fs.writeFile(
      `${ROOT}/src/web/i18n/fr.ts`,
      `export default { "home.title": "Accueil", "home.unused": "Rien" };`,
    );
    await env.fs.writeFile(
      `${ROOT}/src/web/i18n/en.ts`,
      `export default { "home.title": "Home", "home.unused": "Nothing" };`,
    );
    // The only reference — `home.unused` is dead in both languages. The
    // `"home.title": "…"` lines in the sibling language file must not count
    // as references (key files are excluded from the usage corpus).
    await env.fs.writeFile(
      `${ROOT}/src/web/Home.tsx`,
      `export const Home = () => tr("home.title");`,
    );

    const result = await env.service.check({
      root: ROOT,
      scan: ["src"],
      dynamicPrefixes: [],
      exclude: [],
    });

    expect(result.totalKeys).toBe(2);
    expect(result.unused).toEqual(["home.unused"]);
    // The resolved language file is treated as a dictionary; the sibling
    // `$page` lazy import is not (it contributed no keys and stays in the
    // usage corpus). `en.ts` is also a dictionary file but adds no *new*
    // keys, so only the first contributor is listed.
    expect(result.dictionaryFiles).toContain(`${ROOT}/src/web/i18n/fr.ts`);
    expect(result.dictionaryFiles).not.toContain(`${ROOT}/src/web/Home.tsx`);
  });

  it("returns totalKeys=0 when no dictionary is found", async () => {
    await env.fs.mkdir(`${ROOT}/src`, { recursive: true });
    await env.fs.writeFile(
      `${ROOT}/src/App.tsx`,
      `export const App = () => "hi";`,
    );

    const result = await env.service.check({
      root: ROOT,
      scan: ["src"],
      dynamicPrefixes: [],
      exclude: [],
    });

    expect(result.totalKeys).toBe(0);
  });
});
