/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
import { gzipSync } from "node:zlib";
import postcss from "postcss";
//#region ../css/dist/utils/optimizeCSS.mjs
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
/**
* Check if a selector is a "meaningful" selector (class, ID, attribute, or pseudo-class)
* as opposed to just element/type selectors (like `code, kbd, pre` in preflight).
* Meaningful selectors indicate the font is actually used by components, not just base reset.
*/
function isMeaningfulSelector(selector) {
	const parts = selector.split(",").map((s) => s.trim());
	for (const part of parts) if (part.includes(".") || part.includes("#") || part.includes("[") || part.includes(":not(") || part.includes(":where(") || part.includes(":is(") || part.includes(":has(")) return true;
	return false;
}
/**
* PostCSS plugin to remove @font-face declarations for font families that are not used
* anywhere in the CSS (excluding preflight/reset styles that only use element selectors).
*/
function removeUnusedFontFaces() {
	return {
		postcssPlugin: "remove-unused-font-faces",
		Once(root) {
			const fontVarUsage = /* @__PURE__ */ new Map();
			const usedFontFamilies = /* @__PURE__ */ new Set();
			root.walkDecls("font-family", (decl) => {
				if (decl.parent?.type === "atrule" && decl.parent.name === "font-face") return;
				const meaningful = isMeaningfulSelector(decl.parent?.selector || "");
				const value = decl.value;
				const varMatches = value.matchAll(/var\(--uds-font-([^),]+)\)/g);
				for (const match of varMatches) {
					const varName = match[1];
					const existing = fontVarUsage.get(varName) || {
						used: false,
						meaningful: false
					};
					fontVarUsage.set(varName, {
						used: true,
						meaningful: existing.meaningful || meaningful
					});
				}
				if (meaningful) value.split(",").map((font) => font.trim()).map((font) => {
					if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) return font.slice(1, -1);
					if (font.startsWith("var(") || [
						"serif",
						"sans-serif",
						"monospace",
						"cursive",
						"fantasy",
						"system-ui",
						"ui-monospace",
						"ui-serif",
						"ui-sans-serif",
						"ui-rounded"
					].includes(font.toLowerCase())) return null;
					return font;
				}).filter(Boolean).forEach((name) => usedFontFamilies.add(name));
			});
			const fontVarDefinitions = /* @__PURE__ */ new Map();
			root.walkDecls((decl) => {
				if (decl.prop.startsWith("--uds-font-") && !decl.prop.includes("size") && !decl.prop.includes("weight") && !decl.prop.includes("slant") && !decl.prop.includes("width")) {
					const varName = decl.prop.replace("--uds-font-", "");
					fontVarDefinitions.set(varName, decl.value);
				}
			});
			const resolveToFontNames = (value, visited = /* @__PURE__ */ new Set()) => {
				const fontNames = [];
				const varMatch = value.match(/var\(--uds-font-([^),]+)\)/);
				if (varMatch) {
					const nestedVarName = varMatch[1];
					if (!visited.has(nestedVarName)) {
						visited.add(nestedVarName);
						const nestedValue = fontVarDefinitions.get(nestedVarName);
						if (nestedValue) {
							const nestedUsage = fontVarUsage.get(nestedVarName);
							if (nestedUsage) {
								if (fontVarUsage.get(value.replace(/var\(--uds-font-([^),]+)\).*/, "$1"))?.meaningful) nestedUsage.meaningful = true;
							}
							fontNames.push(...resolveToFontNames(nestedValue, visited));
						}
					}
				}
				value.split(",").map((font) => font.trim()).forEach((font) => {
					if (font.startsWith("var(")) return;
					if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) font = font.slice(1, -1);
					if ([
						"serif",
						"sans-serif",
						"monospace",
						"cursive",
						"fantasy",
						"system-ui",
						"ui-monospace",
						"ui-serif",
						"ui-sans-serif",
						"ui-rounded"
					].includes(font.toLowerCase())) return;
					if (font) fontNames.push(font);
				});
				return fontNames;
			};
			for (const [varName, value] of fontVarDefinitions) {
				const usage = fontVarUsage.get(varName);
				if (usage?.used && usage?.meaningful) resolveToFontNames(value).forEach((name) => usedFontFamilies.add(name));
			}
			const keptFonts = /* @__PURE__ */ new Set();
			root.walkAtRules("font-face", (atRule) => {
				let fontFamily = null;
				atRule.walkDecls("font-family", (decl) => {
					fontFamily = decl.value.replace(/["']/g, "").trim();
				});
				if (fontFamily && !usedFontFamilies.has(fontFamily)) atRule.remove();
				else if (fontFamily) keptFonts.add(fontFamily);
			});
			root.walkDecls((decl) => {
				if (decl.prop.startsWith("--uds-font-") && !decl.prop.includes("size") && !decl.prop.includes("weight") && !decl.prop.includes("slant") && !decl.prop.includes("width")) {
					const fonts = decl.value.split(",").map((f) => f.trim());
					const filteredFonts = fonts.filter((font) => {
						if (font.startsWith("var(")) return true;
						let fontName = font;
						if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) fontName = font.slice(1, -1);
						if ([
							"serif",
							"sans-serif",
							"monospace",
							"cursive",
							"fantasy",
							"system-ui",
							"ui-monospace",
							"ui-serif",
							"ui-sans-serif",
							"ui-rounded"
						].includes(fontName.toLowerCase())) return true;
						return keptFonts.has(fontName);
					});
					if (filteredFonts.length < fonts.length && filteredFonts.length > 0) decl.value = filteredFonts.join(", ");
				}
			});
		}
	};
}
removeUnusedFontFaces.postcss = true;
/**
* PostCSS plugin to remove redundant .uds-color-mode-light rules.
* Light mode is the default (set on :root), so the explicit class is unnecessary.
*/
function removeRedundantLightModePlugin() {
	return {
		postcssPlugin: "remove-redundant-light-mode",
		Once(root) {
			root.walkRules((rule) => {
				const selectors = rule.selector.split(",").map((s) => s.trim());
				const filteredSelectors = selectors.filter((s) => {
					const normalized = s.replace(/\s+/g, " ").trim();
					return normalized !== ".uds-color-mode-light" && normalized !== ":root .uds-color-mode-light" && normalized !== ":where(.uds-color-mode-light)" && normalized !== ":is(.uds-color-mode-light)";
				});
				if (filteredSelectors.length === 0) rule.remove();
				else if (filteredSelectors.length < selectors.length) rule.selector = filteredSelectors.join(", ");
			});
		}
	};
}
removeRedundantLightModePlugin.postcss = true;
/**
* PostCSS plugin to remove empty CSS rules (rules with no declarations).
* Also aggregates duplicate selectors.
*/
function removeEmptyRulesPlugin() {
	return {
		postcssPlugin: "remove-empty-rules",
		Once(root) {
			root.walkRules((rule) => {
				if (!rule.nodes?.some((node) => node.type === "decl" || node.type === "rule" && node.nodes?.length > 0)) rule.remove();
			});
			root.walkAtRules((atRule) => {
				if (atRule.name !== "font-face" && atRule.name !== "keyframes") {
					if (!atRule.nodes?.some((node) => {
						if (node.type === "rule") return node.nodes?.some((n) => n.type === "decl");
						if (node.type === "decl") return true;
						return false;
					}) && atRule.nodes?.length === 0) atRule.remove();
				}
			});
		}
	};
}
removeEmptyRulesPlugin.postcss = true;
/**
* PostCSS plugin to aggregate rules with identical selectors.
*/
function aggregateDuplicateSelectorsPlugin() {
	return {
		postcssPlugin: "aggregate-duplicate-selectors",
		Once(root) {
			const selectorMap = /* @__PURE__ */ new Map();
			const rulesToProcess = [];
			root.walkRules((rule) => {
				if (rule.parent?.type === "root" || rule.parent?.type === "atrule") rulesToProcess.push(rule);
			});
			for (const rule of rulesToProcess) {
				const selector = rule.selector;
				const parent = rule.parent;
				const key = `${parent?.type === "atrule" ? `@${parent.name}:${parent.params}` : "root"}|${selector}`;
				if (selectorMap.has(key)) {
					const existingRule = selectorMap.get(key);
					rule.walkDecls((decl) => {
						const existingDecl = existingRule.nodes?.find((node) => node.type === "decl" && node.prop === decl.prop);
						if (existingDecl) existingDecl.value = decl.value;
						else existingRule.append(decl.clone());
					});
					rule.remove();
				} else selectorMap.set(key, rule);
			}
		}
	};
}
aggregateDuplicateSelectorsPlugin.postcss = true;
/**
* Validates CSS syntax and returns any errors found.
*/
function validateCSS(css) {
	const errors = [];
	try {
		const result = postcss.parse(css);
		result.walkRules((rule) => {
			if (!rule.selector.trim()) errors.push(`Empty selector found at line ${rule.source?.start?.line}`);
			try {
				if (rule.selector.includes("{{") || rule.selector.includes("}}")) errors.push(`Malformed selector "${rule.selector}" at line ${rule.source?.start?.line}`);
			} catch {
				errors.push(`Invalid selector "${rule.selector}" at line ${rule.source?.start?.line}`);
			}
		});
		result.walkDecls((decl) => {
			if (!decl.value.trim()) errors.push(`Empty value for property "${decl.prop}" at line ${decl.source?.start?.line}`);
		});
		return {
			valid: errors.length === 0,
			errors
		};
	} catch (error) {
		if (error instanceof Error) errors.push(`CSS Parse Error: ${error.message}`);
		else errors.push("Unknown CSS parse error");
		return {
			valid: false,
			errors
		};
	}
}
/**
* Create a fingerprint for a @font-face rule
*/
function getFontFaceFingerprint(atRule) {
	let fontFamily = "";
	let src = "";
	let fontWeight = "normal";
	let fontStyle = "normal";
	atRule.walkDecls((decl) => {
		if (decl.prop === "font-family") fontFamily = decl.value.replace(/["']/g, "").trim();
		else if (decl.prop === "src") src = decl.value;
		else if (decl.prop === "font-weight") fontWeight = decl.value;
		else if (decl.prop === "font-style") fontStyle = decl.value;
	});
	return JSON.stringify({
		fontFamily,
		src,
		fontWeight,
		fontStyle
	});
}
/**
* Create a fingerprint for a @keyframes rule
*/
function getKeyframesFingerprint(atRule) {
	const name = atRule.params;
	const content = atRule.toString().replace(/\s+/g, " ").trim();
	return JSON.stringify({
		name,
		content
	});
}
/**
* Normalize a selector by removing scope prefix and whitespace
*/
function normalizeSelector(selector, scopeClass) {
	let normalized = selector.trim();
	if (scopeClass) {
		const escapedScope = scopeClass.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
		normalized = normalized.replace(new RegExp(`${escapedScope}\\s+`, "g"), "").replace(new RegExp(`${escapedScope}\\.`, "g"), ".").replace(new RegExp(`${escapedScope}`, "g"), "");
	}
	return normalized.replace(/\s+/g, " ").trim();
}
/**
* Create a fingerprint for a CSS rule (selector + declarations)
*/
function getRuleFingerprint(rule, scopeClass) {
	const selector = normalizeSelector(rule.selector, scopeClass);
	const declarations = [];
	rule.walkDecls((decl) => {
		declarations.push(`${decl.prop}:${decl.value}`);
	});
	declarations.sort();
	return JSON.stringify({
		selector,
		declarations
	});
}
/**
* Extract reference data from CSS for deduplication
*/
function extractCSSReferenceData(css, scopeClass) {
	const data = {
		fontFaces: /* @__PURE__ */ new Set(),
		keyframes: /* @__PURE__ */ new Set(),
		rules: /* @__PURE__ */ new Set()
	};
	try {
		const root = postcss.parse(css);
		root.walkAtRules((atRule) => {
			if (atRule.name === "font-face") data.fontFaces.add(getFontFaceFingerprint(atRule));
			else if (atRule.name === "keyframes" || atRule.name === "-webkit-keyframes") data.keyframes.add(getKeyframesFingerprint(atRule));
		});
		root.walkRules((rule) => {
			if (rule.parent?.type === "atrule") {
				const parentName = rule.parent.name;
				if (parentName === "font-face" || parentName === "keyframes" || parentName === "-webkit-keyframes") return;
			}
			data.rules.add(getRuleFingerprint(rule, scopeClass));
		});
	} catch {}
	return data;
}
/**
* PostCSS plugin to remove CSS that exists in reference CSS
* Handles @font-face, @keyframes, and regular rule deduplication
*/
function removeDuplicateCss(referenceData, scopeClass) {
	return {
		postcssPlugin: "remove-duplicate-css",
		Once(root) {
			const toRemove = [];
			root.walkAtRules((atRule) => {
				if (atRule.name === "font-face") {
					const fingerprint = getFontFaceFingerprint(atRule);
					if (referenceData.fontFaces.has(fingerprint)) toRemove.push(atRule);
				} else if (atRule.name === "keyframes" || atRule.name === "-webkit-keyframes") {
					const fingerprint = getKeyframesFingerprint(atRule);
					if (referenceData.keyframes.has(fingerprint)) toRemove.push(atRule);
				}
			});
			root.walkRules((rule) => {
				if (rule.parent?.type === "atrule") {
					const parentName = rule.parent.name;
					if (parentName === "font-face" || parentName === "keyframes" || parentName === "-webkit-keyframes") return;
				}
				const fingerprint = getRuleFingerprint(rule, scopeClass);
				if (referenceData.rules.has(fingerprint)) toRemove.push(rule);
			});
			for (const node of toRemove) node.remove();
		}
	};
}
/**
* Optimizes CSS by:
* 1. Removing unused @font-face declarations
* 2. Removing empty rules
* 3. Aggregating duplicate selectors
*
* Returns the optimized CSS and validation results.
*/
async function optimizeCSS(css, options) {
	const originalSize = Buffer.byteLength(css, "utf8");
	const originalSizeGzip = gzipSync(css).length;
	const fontFacesBefore = (css.match(/@font-face/g) || []).length;
	const rulesBefore = (css.match(/\{[^}]*\}/g) || []).length;
	const plugins = [];
	if (options?.removeUnusedFonts !== false) plugins.push(removeUnusedFontFaces());
	plugins.push(removeRedundantLightModePlugin());
	if (options?.referenceCss) {
		const referenceData = extractCSSReferenceData(options.referenceCss);
		if (referenceData.fontFaces.size > 0 || referenceData.keyframes.size > 0 || referenceData.rules.size > 0) plugins.push(removeDuplicateCss(referenceData, options.scopeClass));
	}
	if (options?.removeEmptyRules !== false) plugins.push(removeEmptyRulesPlugin());
	if (options?.aggregateDuplicateSelectors !== false) plugins.push(aggregateDuplicateSelectorsPlugin());
	const optimizedCSS = (plugins.length > 0 ? await postcss(plugins).process(css, { from: void 0 }) : { css }).css;
	const optimizedSize = Buffer.byteLength(optimizedCSS, "utf8");
	const fontFacesAfter = (optimizedCSS.match(/@font-face/g) || []).length;
	const rulesAfter = (optimizedCSS.match(/\{[^}]*\}/g) || []).length;
	return {
		css: optimizedCSS,
		validation: validateCSS(optimizedCSS),
		stats: {
			originalSize,
			originalSizeGzip,
			optimizedSize,
			fontFacesRemoved: fontFacesBefore - fontFacesAfter,
			emptyRulesRemoved: Math.max(0, rulesBefore - rulesAfter)
		}
	};
}
//#endregion
export { optimizeCSS };
