import { describe, expect, it } from "vitest";
import {
	fontList,
	getFont,
	getFontDisplayName,
	getFontSearchKeywords,
	getPdfFallbackFontFamilies,
	getWebFont,
	getWebFontSource,
	isStandardPdfFontFamily,
	resolveLegacyFontAlias,
	standardFontList,
	webFontList,
	webFontMap,
} from "./index";

const sortFontFamilies = (families: string[]) => {
	return [...families].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
};

describe("fontList", () => {
	it("is ordered by font family name instead of localized display name", () => {
		const families = fontList.map((font) => font.family);
		expect(families).toEqual(sortFontFamilies(families));
	});

	it("contains standard PDF fonts", () => {
		const families = fontList.map((font) => font.family);
		expect(families).toContain("Helvetica");
		expect(families).toContain("Courier");
		expect(families).toContain("Times-Roman");
	});

	it("merges standardFontList and webFontList without duplicates", () => {
		const expectedSize = standardFontList.length + webFontList.length;
		expect(fontList).toHaveLength(expectedSize);
	});
});

describe("standardFontList", () => {
	it("contains only fonts not present in webFontMap", () => {
		for (const font of standardFontList) {
			expect(webFontMap.has(font.family)).toBe(false);
		}
	});

	it("marks all standard fonts with type='standard'", () => {
		for (const font of standardFontList) {
			expect(font.type).toBe("standard");
		}
	});
});

describe("getFont", () => {
	it("returns a font record by family", () => {
		expect(getFont("Helvetica")).toBeDefined();
		expect(getFont("Helvetica")?.family).toBe("Helvetica");
	});

	it("returns undefined for unknown families", () => {
		expect(getFont("DefinitelyNotAFont")).toBeUndefined();
	});
});

describe("getFontDisplayName", () => {
	it("returns localized name when available", () => {
		expect(getFontDisplayName("Noto Sans SC")).toBe("思源黑体");
		expect(getFontDisplayName("PingFang SC")).toBe("苹方");
	});

	it("returns the family name when no localized name exists", () => {
		expect(getFontDisplayName("Helvetica")).toBe("Helvetica");
		expect(getFontDisplayName("Roboto")).toBe("Roboto");
	});
});

describe("getFontSearchKeywords", () => {
	it("returns family for non-CJK fonts", () => {
		const keywords = getFontSearchKeywords("Helvetica");
		expect(keywords).toContain("Helvetica");
		expect(keywords).not.toContain("中文");
	});

	it("includes localized display name and 中文 marker for CJK fonts", () => {
		const keywords = getFontSearchKeywords("Noto Sans SC");
		expect(keywords).toContain("Noto Sans SC");
		expect(keywords).toContain("思源黑体");
		expect(keywords).toContain("中文");
	});

	it("deduplicates entries", () => {
		const keywords = getFontSearchKeywords("Helvetica");
		expect(new Set(keywords).size).toBe(keywords.length);
	});
});

describe("isStandardPdfFontFamily", () => {
	it("returns true for Helvetica, Courier, Times-Roman", () => {
		expect(isStandardPdfFontFamily("Helvetica")).toBe(true);
		expect(isStandardPdfFontFamily("Courier")).toBe(true);
		expect(isStandardPdfFontFamily("Times-Roman")).toBe(true);
	});

	it("returns false for unknown families", () => {
		expect(isStandardPdfFontFamily("Not-A-Font")).toBe(false);
	});

	it("returns false for non-standard web fonts", () => {
		// Roboto is a web font, not a standard PDF font
		const roboto = getWebFont("Roboto");
		if (roboto) expect(isStandardPdfFontFamily("Roboto")).toBe(false);
	});
});

describe("getWebFont", () => {
	it("returns webFont record for known web font family", () => {
		// Spot-check a font that should be in the list
		const font = getWebFont("Roboto");
		if (font) {
			expect(font.type).toBe("web");
			expect(font.family).toBe("Roboto");
		}
	});

	it("returns undefined for non-web families", () => {
		expect(getWebFont("Helvetica")).toBeUndefined();
		expect(getWebFont("definitely-not-a-font")).toBeUndefined();
	});
});

describe("getWebFontSource", () => {
	it("uses the full normal font source when an italic variant is unavailable", () => {
		expect(getWebFontSource("Noto Serif SC", "400", true)).toBe(getWebFontSource("Noto Serif SC", "400", false));
	});

	it("returns null for unknown fonts", () => {
		expect(getWebFontSource("definitely-not-a-font", "400")).toBeNull();
	});

	it("defaults to weight 400 when not specified", () => {
		const roboto = getWebFont("Roboto");
		if (roboto) {
			const source = getWebFontSource("Roboto");
			expect(source).toBeTruthy();
		}
	});
});

describe("getPdfFallbackFontFamilies", () => {
	it("puts the Korean Noto font first for the ko-KR locale (Hangul needs KR, not SC)", () => {
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "ko-KR" })).toEqual(["Noto Serif KR", "Noto Serif SC"]);
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "ko-KR" })).toEqual(["Noto Sans KR", "Noto Sans SC"]);
	});

	it("uses the Japanese Noto font for the ja-JP locale", () => {
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "ja-JP" })).toEqual(["Noto Serif JP", "Noto Serif SC"]);
	});

	it("uses the Traditional Chinese Noto font for the zh-TW locale", () => {
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "zh-TW" })).toEqual(["Noto Serif TC", "Noto Serif SC"]);
	});

	it("returns only the Simplified Chinese font for zh-CN (unchanged behavior)", () => {
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "zh-CN" })).toEqual(["Noto Serif SC"]);
	});

	it("uses the Arabic Noto font for the fa-IR (Persian) and ar-SA locales", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "fa-IR" })).toEqual(["Noto Sans Arabic"]);
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "ar-SA" })).toEqual(["Noto Naskh Arabic"]);
	});

	it("uses the Hebrew Noto font for he-IL, reusing the sans font for serif (no Noto Serif Hebrew)", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "he-IL" })).toEqual(["Noto Sans Hebrew"]);
		expect(getPdfFallbackFontFamilies("Times-Roman", { locale: "he-IL" })).toEqual(["Noto Sans Hebrew"]);
	});

	it("uses the Thai Noto font for th-TH", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "th-TH" })).toEqual(["Noto Sans Thai"]);
	});

	it("does not append the Simplified Chinese safety net for non-CJK scripts", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { scripts: ["arabic"] })).toEqual(["Noto Sans Arabic"]);
		expect(getPdfFallbackFontFamilies("Helvetica", { scripts: ["thai"] })).not.toContain("Noto Sans SC");
	});

	it("orders the locale script first, then content scripts (mixed RTL + CJK resume)", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "ko-KR", scripts: ["arabic"] })).toEqual([
			"Noto Sans KR",
			"Noto Sans Arabic",
			"Noto Sans SC",
		]);
	});

	it("includes a Korean font before SC when Hangul is detected in Latin-locale content", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { scripts: ["hangul"] })).toEqual(["Noto Sans KR", "Noto Sans SC"]);
	});

	it("dedupes the locale script and content scripts", () => {
		expect(getPdfFallbackFontFamilies("Helvetica", { locale: "ko-KR", scripts: ["hangul", "han-simplified"] })).toEqual(
			["Noto Sans KR", "Noto Sans SC"],
		);
	});

	it("excludes the family itself when it already is a fallback", () => {
		expect(getPdfFallbackFontFamilies("Noto Sans KR", { locale: "ko-KR" })).toEqual(["Noto Sans SC"]);
	});

	it("only returns fonts that exist in the webfontlist", () => {
		const families = getPdfFallbackFontFamilies("Helvetica", {
			locale: "ko-KR",
			scripts: ["hangul", "kana", "han-simplified", "arabic", "hebrew", "thai"],
		});
		for (const family of families) {
			expect(getWebFont(family)).toBeDefined();
		}
	});
});

describe("legacy font compatibility (#2989)", () => {
	it.each([
		["Times New Roman", "Times-Roman"],
		["Arial", "Arimo"],
		["Garamond", "EB Garamond"],
		["Calibri", "Carlito"],
		["Cambria", "Tinos"],
	])("aliases %s → %s", (legacy, target) => {
		expect(resolveLegacyFontAlias(legacy)).toBe(target);
	});

	it("returns null for non-aliased families", () => {
		expect(resolveLegacyFontAlias("Roboto")).toBeNull();
		expect(resolveLegacyFontAlias("IBM Plex Serif")).toBeNull();
		expect(resolveLegacyFontAlias("UnknownFont")).toBeNull();
	});

	it("every alias target is actually registered as a known font", () => {
		const aliasTargets = ["Times-Roman", "Tinos", "Arimo", "EB Garamond", "Carlito"];
		for (const target of aliasTargets) {
			expect(getFont(target), `alias target ${target} must be a known font`).toBeDefined();
		}
	});

	it("getFont resolves a legacy family to its alias target", () => {
		const tnr = getFont("Times New Roman");
		expect(tnr).toBeDefined();
		expect(tnr?.family).toBe("Times-Roman");
	});

	it("getFont still returns the direct font when both legacy and direct lookup would succeed", () => {
		// Sanity check: for non-aliased families the direct path is used.
		expect(getFont("Roboto")?.family).toBe("Roboto");
	});

	it("getFontDisplayName preserves the legacy family name (UI is not rewritten)", () => {
		// Users who picked "Times New Roman" should keep seeing that label
		// in the typography sidebar — the alias is a render-time concern only.
		expect(getFontDisplayName("Times New Roman")).toBe("Times New Roman");
	});
});
