/** @license MIT | @cube-dev/ui-kit v0.115.0 | Cube Dev Team */
import { CUSTOM_UNITS, getGlobalFuncs, getGlobalParser, normalizeColorTokenValue, resetGlobalPredefinedTokens, setGlobalPredefinedTokens } from "./utils/styles.js";
import { isDevEnv } from "./utils/is-dev-env.js";
import { normalizeHandlerDefinition, registerHandler, resetHandlers } from "./styles/predefined.js";
import { StyleInjector } from "./injector/injector.js";
import { setGlobalPredefinedStates } from "./states/index.js";
import { clearPipelineCache, isSelector } from "./pipeline/index.js";

//#region src/tasty/config.ts
/**
* Tasty Configuration Module
*
* Centralizes all tasty configuration, including:
* - Style injector settings (nonce, cleanup thresholds, etc.)
* - Global predefined states for advanced state mapping
* - stylesGenerated flag that locks configuration after first style generation
*
* Configuration must be done BEFORE any styles are generated.
* After the first `inject()` call, configuration is locked and attempts to
* reconfigure will emit a warning and be ignored.
*/
const emittedWarnings = /* @__PURE__ */ new Set();
const devMode = isDevEnv();
/**
* Emit a warning only once
*/
function warnOnce(key, message) {
	if (devMode && !emittedWarnings.has(key)) {
		emittedWarnings.add(key);
		console.warn(message);
	}
}
let stylesGenerated = false;
let currentConfig = null;
let globalKeyframes = null;
let globalProperties = null;
let globalRecipes = null;
/**
* Internal properties required by tasty core features.
* These are always injected when styles are first generated.
* Keys use tasty token syntax (#name for colors, $name for other properties).
*
* For properties with CSS @property-compatible types (length, time, number, color),
* an `initialValue` is provided so the property works even without a project-level token.
*/
const INTERNAL_PROPERTIES = {
	"#tasty-second-fill": {
		inherits: false,
		initialValue: "transparent"
	},
	"#current": {
		inherits: true,
		initialValue: "transparent"
	},
	"#white": {
		inherits: true,
		initialValue: "rgb(255 255 255)"
	},
	"#black": {
		inherits: true,
		initialValue: "rgb(0 0 0)"
	},
	$gap: {
		syntax: "<length>",
		inherits: true,
		initialValue: "4px"
	},
	$radius: {
		syntax: "<length>",
		inherits: true,
		initialValue: "6px"
	},
	"$border-width": {
		syntax: "<length>",
		inherits: true,
		initialValue: "1px"
	},
	"$outline-width": {
		syntax: "<length>",
		inherits: true,
		initialValue: "3px"
	},
	$transition: {
		syntax: "<time>",
		inherits: true,
		initialValue: "80ms"
	},
	"$sharp-radius": {
		syntax: "<length>",
		inherits: true,
		initialValue: "0px"
	},
	"$bold-font-weight": {
		syntax: "<number>",
		inherits: true,
		initialValue: "700"
	}
};
/**
* Internal token defaults that cannot be expressed as CSS @property initial values
* (e.g. font stacks, keyword colors). These are injected as :root CSS variables.
* Consuming projects override them by setting their own tokens on :root.
*
* Keys are raw CSS custom property names (--name).
*/
const INTERNAL_TOKENS = {
	"--font": "system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif",
	"--monospace-font": "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace",
	"--border-color": "currentColor"
};
const GLOBAL_INJECTOR_KEY = "__TASTY_GLOBAL_INJECTOR__";
/**
* Detect if we're running in a test environment
*/
function isTestEnvironment() {
	if (typeof global !== "undefined") {
		const g = global;
		if (g.vi || g.jest || g.expect || g.describe || g.it) return true;
	}
	if (typeof window !== "undefined" && window.navigator?.userAgent?.includes("jsdom")) return true;
	if (typeof globalThis !== "undefined") {
		const gt = globalThis;
		if (gt.vitest || gt.mocha) return true;
	}
	return false;
}
/**
* Create default configuration with optional test environment detection
*/
function createDefaultConfig(isTest) {
	return {
		maxRulesPerSheet: 8192,
		unusedStylesThreshold: 500,
		bulkCleanupDelay: 5e3,
		idleCleanup: true,
		forceTextInjection: isTest ?? false,
		devMode: isDevEnv(),
		bulkCleanupBatchRatio: .5,
		unusedStylesMinAgeMs: 1e4
	};
}
/**
* Mark that styles have been generated (called by injector on first inject)
* This locks the configuration - no further changes allowed.
* Also injects internal and global properties.
*/
function markStylesGenerated() {
	if (stylesGenerated) return;
	stylesGenerated = true;
	const injector = getGlobalInjector();
	for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) injector.property(token, definition);
	const internalTokenEntries = Object.entries(INTERNAL_TOKENS);
	if (internalTokenEntries.length > 0) {
		const declarations = internalTokenEntries.map(([name, value]) => `${name}: ${value}`).join("; ");
		injector.injectGlobal([{
			selector: ":root",
			declarations
		}]);
	}
	if (globalProperties && Object.keys(globalProperties).length > 0) for (const [token, definition] of Object.entries(globalProperties)) injector.property(token, definition);
}
/**
* Check if styles have been generated (configuration is locked)
*/
function hasStylesGenerated() {
	return stylesGenerated;
}
/**
* Check if any global keyframes are configured.
* Fast path: returns false if no keyframes were ever set.
*/
function hasGlobalKeyframes() {
	return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;
}
/**
* Get global keyframes configuration.
* Returns null if no keyframes configured (fast path for zero-overhead).
*/
function getGlobalKeyframes() {
	return globalKeyframes;
}
/**
* Set global keyframes (called from configure).
* Internal use only.
*/
function setGlobalKeyframes(keyframes) {
	if (stylesGenerated) {
		warnOnce("keyframes-after-styles", "[Tasty] Cannot update keyframes after styles have been generated.\nThe new keyframes will be ignored.");
		return;
	}
	globalKeyframes = keyframes;
}
/**
* Set global properties (called from configure).
* Internal use only.
*/
function setGlobalProperties(properties) {
	if (stylesGenerated) {
		warnOnce("properties-after-styles", "[Tasty] Cannot update properties after styles have been generated.\nThe new properties will be ignored.");
		return;
	}
	globalProperties = properties;
}
/**
* Check if any global recipes are configured.
* Fast path: returns false if no recipes were ever set.
*/
function hasGlobalRecipes() {
	return globalRecipes !== null && Object.keys(globalRecipes).length > 0;
}
/**
* Get global recipes configuration.
* Returns null if no recipes configured (fast path for zero-overhead).
*/
function getGlobalRecipes() {
	return globalRecipes;
}
/**
* Set global recipes (called from configure).
* Internal use only.
*/
function setGlobalRecipes(recipes) {
	if (stylesGenerated) {
		warnOnce("recipes-after-styles", "[Tasty] Cannot update recipes after styles have been generated.\nThe new recipes will be ignored.");
		return;
	}
	if (devMode) for (const [name, recipeStyles] of Object.entries(recipes)) for (const key of Object.keys(recipeStyles)) {
		if (isSelector(key)) warnOnce(`recipe-selector-${name}-${key}`, `[Tasty] Recipe "${name}" contains sub-element key "${key}". Recipes must be flat styles without sub-element keys. Remove the sub-element key from the recipe definition.`);
		if (key === "recipe") warnOnce(`recipe-recursive-${name}`, `[Tasty] Recipe "${name}" contains a "recipe" key. Recipes cannot reference other recipes. Use space-separated names for composition: recipe: 'base elevated'.`);
	}
	globalRecipes = recipes;
}
/**
* Check if configuration is locked (styles have been generated)
*/
function isConfigLocked() {
	return stylesGenerated;
}
/**
* Configure the Tasty style system.
*
* Must be called BEFORE any styles are generated (before first render that uses tasty).
* After styles are generated, configuration is locked and calls to configure() will
* emit a warning and be ignored.
*
* @example
* ```ts
* import { configure } from '@cube-dev/ui-kit';
*
* // Configure before app renders
* configure({
*   nonce: 'abc123',
*   states: {
*     '@mobile': '@media(w < 768px)',
*     '@dark': '@root(theme=dark)',
*   },
* });
* ```
*/
function configure(config = {}) {
	if (stylesGenerated) {
		warnOnce("configure-after-styles", "[Tasty] Cannot call configure() after styles have been generated.\nConfiguration must be done before the first render. The configuration will be ignored.");
		return;
	}
	let mergedStates = {};
	let mergedUnits = {};
	let mergedFuncs = {};
	let mergedHandlers = {};
	let mergedTokens = {};
	let mergedRecipes = {};
	if (config.plugins) for (const plugin of config.plugins) {
		if (plugin.states) mergedStates = {
			...mergedStates,
			...plugin.states
		};
		if (plugin.units) mergedUnits = {
			...mergedUnits,
			...plugin.units
		};
		if (plugin.funcs) mergedFuncs = {
			...mergedFuncs,
			...plugin.funcs
		};
		if (plugin.handlers) mergedHandlers = {
			...mergedHandlers,
			...plugin.handlers
		};
		if (plugin.tokens) mergedTokens = {
			...mergedTokens,
			...plugin.tokens
		};
		if (plugin.recipes) mergedRecipes = {
			...mergedRecipes,
			...plugin.recipes
		};
	}
	if (config.states) mergedStates = {
		...mergedStates,
		...config.states
	};
	if (config.units) mergedUnits = {
		...mergedUnits,
		...config.units
	};
	if (config.funcs) mergedFuncs = {
		...mergedFuncs,
		...config.funcs
	};
	if (config.handlers) mergedHandlers = {
		...mergedHandlers,
		...config.handlers
	};
	if (config.tokens) mergedTokens = {
		...mergedTokens,
		...config.tokens
	};
	if (config.recipes) mergedRecipes = {
		...mergedRecipes,
		...config.recipes
	};
	if (Object.keys(mergedStates).length > 0) setGlobalPredefinedStates(mergedStates);
	const parser = getGlobalParser();
	if (config.parserCacheSize !== void 0) parser.updateOptions({ cacheSize: config.parserCacheSize });
	if (Object.keys(mergedUnits).length > 0) {
		const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;
		parser.setUnits({
			...currentUnits,
			...mergedUnits
		});
	}
	if (Object.keys(mergedFuncs).length > 0) {
		const currentFuncs = getGlobalFuncs();
		const finalFuncs = {
			...currentFuncs,
			...mergedFuncs
		};
		parser.setFuncs(finalFuncs);
		Object.assign(currentFuncs, mergedFuncs);
	}
	if (config.keyframes) setGlobalKeyframes(config.keyframes);
	if (config.properties) setGlobalProperties(config.properties);
	if (Object.keys(mergedHandlers).length > 0) for (const [name, definition] of Object.entries(mergedHandlers)) registerHandler(normalizeHandlerDefinition(name, definition));
	if (Object.keys(mergedTokens).length > 0) {
		const processedTokens = {};
		for (const [key, value] of Object.entries(mergedTokens)) if (key.startsWith("#")) {
			const normalized = normalizeColorTokenValue(value);
			if (normalized === null) continue;
			processedTokens[key] = String(normalized);
		} else if (value === false) continue;
		else processedTokens[key] = String(value);
		setGlobalPredefinedTokens(processedTokens);
	}
	if (Object.keys(mergedRecipes).length > 0) setGlobalRecipes(mergedRecipes);
	const { states: _states, parserCacheSize: _parserCacheSize, units: _units, funcs: _funcs, plugins: _plugins, keyframes: _keyframes, properties: _properties, handlers: _handlers, tokens: _tokens, recipes: _recipes, ...injectorConfig } = config;
	const fullConfig = {
		...createDefaultConfig(),
		...currentConfig,
		...injectorConfig
	};
	currentConfig = fullConfig;
	const storage = typeof window !== "undefined" ? window : globalThis;
	storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);
}
/**
* Get the current configuration.
* If not configured, returns default configuration.
*/
function getConfig() {
	if (!currentConfig) currentConfig = createDefaultConfig(isTestEnvironment());
	return currentConfig;
}
/**
* Get the global injector instance.
* Auto-configures with defaults if not already configured.
*/
function getGlobalInjector() {
	const storage = typeof window !== "undefined" ? window : globalThis;
	if (!storage[GLOBAL_INJECTOR_KEY]) configure();
	return storage[GLOBAL_INJECTOR_KEY];
}
/**
* Reset configuration (for testing only).
* Clears the global injector and allows reconfiguration.
*/
function resetConfig() {
	stylesGenerated = false;
	currentConfig = null;
	globalKeyframes = null;
	globalProperties = null;
	globalRecipes = null;
	resetGlobalPredefinedTokens();
	resetHandlers();
	clearPipelineCache();
	emittedWarnings.clear();
	const storage = typeof window !== "undefined" ? window : globalThis;
	delete storage[GLOBAL_INJECTOR_KEY];
}

//#endregion
export { configure, getConfig, getGlobalInjector, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
//# sourceMappingURL=config.js.map