import { describe, expect, it } from "vitest";
import { isAppLocale, supportedLocales } from "@/i18n/config";
import { getMessages, mergeLocalizedMessages, translate } from "@/i18n/messages";

function flattenKeys(value: unknown, prefix = ""): string[] {
  if (typeof value === "string") {
    return [prefix];
  }

  if (!value || typeof value !== "object") {
    return [];
  }

  return Object.entries(value).flatMap(([key, nested]) => {
    const next = prefix ? `${prefix}.${key}` : key;
    return flattenKeys(nested, next);
  });
}

describe("i18n messages", () => {
  it("only accepts configured locales", () => {
    expect(isAppLocale("en")).toBe(true);
    expect(isAppLocale("es")).toBe(true);
    expect(isAppLocale("fr")).toBe(true);
    expect(isAppLocale("pt")).toBe(true);
    expect(isAppLocale("de")).toBe(false);
  });

  it("resolves typed catalog entries", () => {
    expect(translate(getMessages("en"), "Home.trySdp")).toBe("Try SDP");
    expect(translate(getMessages("es"), "Home.contactUs")).toBe("Contáctanos");
    expect(translate(getMessages("fr"), "Home.contactUs")).toBe("Nous contacter");
    expect(translate(getMessages("pt"), "Home.contactUs")).toBe("Fale conosco");
  });

  it("keeps non-English catalogs inventory-matched to English", () => {
    const englishKeys = flattenKeys(getMessages("en")).sort();

    for (const locale of supportedLocales) {
      if (locale === "en") continue;
      expect(flattenKeys(getMessages(locale)).sort()).toEqual(englishKeys);
    }
  });

  it("falls back to English while release automation catches up", () => {
    expect(
      mergeLocalizedMessages(
        {
          Home: {
            title: "English title",
            description: "New English-only copy",
          },
        },
        {
          Home: {
            title: "Titre français",
          },
        }
      )
    ).toEqual({
      Home: {
        title: "Titre français",
        description: "New English-only copy",
      },
    });
  });

  it("keeps catalogs free of ICU syntax translate cannot render", () => {
    // translate only substitutes {name}. An ICU construct such as
    // {count, plural, one {#} other {#}} matches nothing, throws nothing, and
    // reaches the user verbatim, so no catalog may contain one.
    for (const locale of supportedLocales) {
      const messages = getMessages(locale) as unknown;
      const offenders = flattenKeys(messages).filter((key) => {
        const value = key.split(".").reduce<unknown>((carry, segment) => {
          return carry && typeof carry === "object"
            ? (carry as Record<string, unknown>)[segment]
            : undefined;
        }, messages);
        return (
          typeof value === "string" && /\{\s*\w+\s*,\s*(plural|select|selectordinal)\b/.test(value)
        );
      });

      expect(offenders).toEqual([]);
    }
  });

  it("rejects missing interpolation values", () => {
    expect(() => translate(getMessages("en"), "DashboardCustody.rotateKey")).toThrow(
      "Missing interpolation value hours for DashboardCustody.rotateKey"
    );
  });
});
