/*! © 2026 Yahoo, Inc. UDS Tailwind and Purger v0.0.0-development */
import { gray, green, magenta } from "../cli/dist/lib/colors.js";
import { print } from "../cli/dist/lib/print.js";
import { spinStart, spinStop } from "../cli/dist/lib/spinner.js";
import { createLogger } from "../cli/dist/lib/logger.js";
import { DEFAULT_ENTRY, resolveEntryPaths } from "../utils/entryPoints.js";
import { findPackageSourceDir, loadConfigFile, scanDirectoriesForSafelist, scanDirectoryForSafelist } from "./nodeUtils.js";
import { defaultTokensConfig } from "../config/dist/index.js";
import { deduplicateSafelist, getThemeAndScaleClasses } from "../purger/optimized/utils/safelist.js";
import { extractVariantDefaults, formatBytes, getConfigurableCssVariables, getMotionVarPrefixes } from "./utils.js";
import { getPruneVarSafelist } from "./generate.helpers.js";
import { generateCSS, generateSimpleModeCSS } from "./generate.js";
import { getMainCssSummaryMessage, getWatchDirectoryGroups } from "./runner.helpers.js";
import fs from "node:fs";
import path from "node:path";

//#region src/css/runner.ts
const SOURCE_FILE_PATTERN = /\.(jsx?|tsx?)$/i;
const getWatchDirs = (dirs) => [...new Set(dirs.filter((dir) => !dir.split(path.sep).includes("node_modules")))];
const isCoveredByRecursiveWatch = (watchPath, targetPath) => targetPath === watchPath || targetPath.startsWith(`${watchPath}${path.sep}`);
const getEntryWatchTargets = (entries) => {
	const directoryTargets = getWatchDirs(entries.filter((entry) => entry.kind === "directory").map((entry) => entry.absolutePath)).map((watchPath) => ({
		watchPath,
		recursive: true
	}));
	const fileNamesByDirectory = /* @__PURE__ */ new Map();
	entries.filter((entry) => entry.kind === "file" && typeof entry.fileName === "string").forEach((entry) => {
		if (directoryTargets.some((target) => isCoveredByRecursiveWatch(target.watchPath, entry.absolutePath))) return;
		const fileNames = fileNamesByDirectory.get(entry.watchDirectory) ?? /* @__PURE__ */ new Set();
		fileNames.add(entry.fileName);
		fileNamesByDirectory.set(entry.watchDirectory, fileNames);
	});
	const fileTargets = [...fileNamesByDirectory.entries()].map(([watchPath, fileNames]) => ({
		watchPath,
		recursive: false,
		fileNames: [...fileNames]
	}));
	return [...directoryTargets, ...fileTargets];
};
const watchSourceFiles = (targets, onFileChange) => {
	targets.forEach((target) => {
		fs.watch(target.watchPath, { recursive: target.recursive }, (_eventType, filename) => {
			if (!filename || !SOURCE_FILE_PATTERN.test(filename)) return;
			if (target.fileNames && !target.fileNames.includes(path.basename(String(filename)))) return;
			onFileChange();
		});
	});
};
const createDebouncedAction = (action, delayMs) => {
	let debounceTimer = null;
	return () => {
		if (debounceTimer) clearTimeout(debounceTimer);
		debounceTimer = setTimeout(action, delayMs);
	};
};
const createQueuedRegenerator = (options) => {
	let isGenerating = false;
	let pendingRegenerate = false;
	const regenerate = async () => {
		if (isGenerating) {
			pendingRegenerate = true;
			return;
		}
		isGenerating = true;
		try {
			options.onStart?.();
			const result = await options.regenerateOnce();
			options.onSuccess?.(result);
		} catch (error) {
			const message = error instanceof Error ? error.message : "CSS generation failed";
			options.onError(message);
		} finally {
			isGenerating = false;
			if (pendingRegenerate) {
				pendingRegenerate = false;
				await regenerate();
			}
		}
	};
	return regenerate;
};
const runCssCommand = async (options, context) => {
	if (!fs.existsSync(path.join(options.workspaceDir, String(options.themeConfigPath)))) {
		await runSimpleMode(options, context);
		return;
	}
	await runThemeMode(options, context);
};
const runSimpleMode = async (options, context) => {
	const entry = options.entryOption ?? DEFAULT_ENTRY;
	const configPath = typeof options.configOption === "string" ? options.configOption : void 0;
	if (!options.watch && !options.silent) spinStart("Generating CSS...");
	try {
		const result = await generateSimpleModeCSS({
			workspaceDir: options.workspaceDir,
			entry,
			outFile: String(options.outFile),
			variants: context.variants,
			autoVariants: context.autoVariants,
			componentData: context.componentData,
			scope: options.scope,
			configPath,
			isWatch: options.watch,
			silent: options.silent
		});
		if (options.watch) await runSimpleModeWatch(options, context, entry, configPath, result.packageDirs ?? []);
	} catch (error) {
		spinStop("❌", error instanceof Error ? error.message : "CSS generation failed");
		process.exitCode = 1;
	}
};
const runSimpleModeWatch = async (options, context, entry, configPath, packageDirs) => {
	const entryWatchTargets = getEntryWatchTargets(resolveEntryPaths(entry, options.workspaceDir));
	const fallbackTargets = getWatchDirs(packageDirs).map((watchPath) => ({
		watchPath,
		recursive: true
	}));
	const watchTargets = packageDirs.length > 0 ? fallbackTargets : entryWatchTargets;
	if (!options.silent) {
		print("");
		print(`${magenta("Watching for changes...")}`);
		watchTargets.forEach((target) => print(`   ${gray("•")} ${target.watchPath}`));
		print(`${gray("Press Ctrl+C to stop")}`);
		print("");
	}
	const regenerate = createQueuedRegenerator({
		onStart: () => {
			if (!options.silent) print(`${gray(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`)} Change detected, regenerating...`);
		},
		regenerateOnce: async () => {
			await generateSimpleModeCSS({
				workspaceDir: options.workspaceDir,
				entry,
				outFile: String(options.outFile),
				variants: context.variants,
				autoVariants: context.autoVariants,
				componentData: context.componentData,
				scope: options.scope,
				configPath,
				isWatch: true,
				silent: true
			});
		},
		onSuccess: () => {
			if (!options.silent) {
				print(`${gray(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`)} ${green("CSS updated")}`);
				print("");
			}
		},
		onError: (message) => {
			print(`Error: ${message}`);
		}
	});
	watchSourceFiles(watchTargets, createDebouncedAction(() => {
		regenerate();
	}, 100));
	await new Promise(() => {});
};
const loadThemeModeSetup = async (options) => {
	const themeConfigInput = await loadConfigFile(String(options.themeConfigPath));
	if (!themeConfigInput) return null;
	const themeContext = {
		cwd: options.workspaceDir,
		watch: options.watch
	};
	const themeConfig = typeof themeConfigInput === "function" ? await themeConfigInput(themeContext) : themeConfigInput;
	let appConfig = defaultTokensConfig;
	if (themeConfig.config) {
		const loadedConfig = await loadConfigFile(themeConfig.config);
		if (loadedConfig) appConfig = loadedConfig;
	}
	return {
		themeConfig,
		colorModes: themeConfig.colorModes ?? ["dark"],
		entries: resolveEntryPaths(themeConfig.entry, options.workspaceDir),
		appConfig,
		appVariantDefaults: extractVariantDefaults(appConfig),
		effectiveSilent: options.silent || themeConfig.silent === true
	};
};
const runThemeMode = async (options, context) => {
	const workspaceDir = options.workspaceDir;
	const outputPath = path.isAbsolute(options.outFile) ? options.outFile : path.join(workspaceDir, String(options.outFile));
	let effectiveSilent = options.silent;
	let log = createLogger({ silent: effectiveSilent });
	log.spinStart("Loading theme configuration...");
	try {
		const setup = await loadThemeModeSetup(options);
		if (!setup) {
			log.spinStop("❌", `Theme config not found: ${options.themeConfigPath}`);
			process.exitCode = 1;
			return;
		}
		const { themeConfig, colorModes, entries, appConfig, appVariantDefaults } = setup;
		effectiveSilent = setup.effectiveSilent;
		log = createLogger({ silent: effectiveSilent });
		const entryDirs = entries.map((entry) => entry.absolutePath);
		if (!effectiveSilent) log.spinStop("✅", "Theme configuration loaded");
		if (themeConfig.config && appConfig === defaultTokensConfig) log.warn(`App config not found: ${themeConfig.config}, using defaults`);
		const packageDirs = [];
		const generateThemeModeCSS = async (opts) => {
			const genStartTime = performance.now();
			const genLog = opts?.isWatch ? createLogger({ silent: true }) : log;
			genLog.spinStart("Scanning app code...");
			const appScanResult = await scanDirectoriesForSafelist(entryDirs, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults);
			genLog.spinStop("✅", `Scanned ${appScanResult.filesScanned} files`);
			genLog.spinStart("Generating main CSS...");
			const inheritedClasses = [...appScanResult.safelist];
			const inheritedComponents = new Set(appScanResult.components);
			const processInheritedPackage = async (packageName) => {
				genLog.spinStart(`Processing package: ${packageName}...`);
				const packageDir = findPackageSourceDir(packageName);
				if (!packageDir) {
					genLog.spinStop("⚠️", `Package not found: ${packageName}`);
					return;
				}
				if (!packageDirs.includes(packageDir)) packageDirs.push(packageDir);
				const packageScanResult = await scanDirectoryForSafelist(packageDir, colorModes, context.variants, context.autoVariants, context.componentData, appVariantDefaults, true);
				inheritedClasses.push(...packageScanResult.safelist);
				packageScanResult.components.forEach((comp) => inheritedComponents.add(comp));
				genLog.spinStop("✅", `${packageName}: ${packageScanResult.filesScanned} files (inherit)`);
			};
			await (themeConfig.inherit ?? []).reduce(async (promise, packageName) => {
				await promise;
				await processInheritedPackage(packageName);
			}, Promise.resolve());
			const mainSafelist = deduplicateSafelist([...inheritedClasses, ...getThemeAndScaleClasses(colorModes)]);
			const allMotionComponents = [...inheritedComponents];
			const mainCssResult = await generateCSS([...themeConfig.css?.safelist ?? [], ...mainSafelist], appConfig, {
				scope: options.scope,
				contentDir: entryDirs,
				cssOptions: themeConfig.css,
				safeVarPrefixes: [
					...getMotionVarPrefixes(context.componentData, allMotionComponents),
					...getConfigurableCssVariables(),
					...getPruneVarSafelist(themeConfig.css)
				]
			});
			const outputDir = path.dirname(outputPath);
			if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
			fs.writeFileSync(outputPath, mainCssResult.css);
			genLog.spinStop("✅", getMainCssSummaryMessage({
				sizeGzipBytes: mainCssResult.sizeGzipBytes,
				optimizationStats: mainCssResult.optimizationStats,
				formatBytes
			}));
			const duration = Math.round(performance.now() - genStartTime);
			genLog.newline();
			genLog.print(green("CSS generation complete!"));
			genLog.newline();
			genLog.label("Output file:", outputPath);
			genLog.newline();
			genLog.print(`${magenta("Total time:")} ${duration}ms`);
			return {
				duration,
				outputPath,
				packageDirs
			};
		};
		await generateThemeModeCSS();
		if (options.watch) {
			const entryWatchTargets = getEntryWatchTargets(entries);
			const { filteredPackageDirs } = getWatchDirectoryGroups(getWatchDirs(entryWatchTargets.map((target) => target.watchPath)), [...entryDirs, ...packageDirs]);
			if (!effectiveSilent) {
				log.newline();
				log.print(`${magenta("Watching for changes...")}`);
				entryDirs.forEach((entryDir) => {
					log.listItem(`App: ${entryDir}`);
				});
				filteredPackageDirs.forEach((dir) => {
					log.listItem(`Package: ${dir}`);
				});
				log.print(`${gray("Press Ctrl+C to stop")}`);
				log.newline();
			}
			const regenerate = createQueuedRegenerator({
				onStart: () => {
					if (!effectiveSilent) {
						const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
						log.print(`${gray(`[${timestamp}]`)} Change detected, regenerating...`);
					}
				},
				regenerateOnce: async () => generateThemeModeCSS({ isWatch: true }),
				onSuccess: (result) => {
					if (!effectiveSilent) {
						const updatedAt = (/* @__PURE__ */ new Date()).toLocaleTimeString();
						log.print(`${gray(`[${updatedAt}]`)} ${green(`CSS updated (${result?.duration}ms)`)}`);
						log.newline();
					}
				},
				onError: (message) => {
					log.print(message);
				}
			});
			const watchDebounce = themeConfig.css?.watchDebounce ?? 100;
			watchSourceFiles([...entryWatchTargets, ...filteredPackageDirs.filter((packageDir) => !entryWatchTargets.some((target) => isCoveredByRecursiveWatch(target.watchPath, packageDir))).map((watchPath) => ({
				watchPath,
				recursive: true
			}))], createDebouncedAction(() => {
				regenerate();
			}, watchDebounce));
			await new Promise(() => {});
		}
	} catch (error) {
		const message = error instanceof Error ? error.message : "CSS generation failed";
		if (effectiveSilent) spinStop("❌", message);
		else log.spinStop("❌", message);
		process.exitCode = 1;
	}
};

//#endregion
export { runCssCommand };
//# sourceMappingURL=runner.js.map