'use strict';

var react = require('react');
var logger = require('@almadar/logger');
var jsxRuntime = require('react/jsx-runtime');

var log = logger.createLogger("almadar:ui:ui-slots");
var DEFAULT_SOURCE_KEY = "__default__";
var MULTI_SOURCE_STACK_TRAIT = "__multi_source_stack__";
var ALL_SLOTS = [
  "main",
  "sidebar",
  "modal",
  "drawer",
  "overlay",
  "center",
  "toast",
  "hud-top",
  "hud-bottom",
  "hud-left",
  "hud-right",
  "floating"
];
var DEFAULT_SLOTS = ALL_SLOTS.reduce(
  (acc, slot) => {
    acc[slot] = null;
    return acc;
  },
  {}
);
var DEFAULT_SOURCES = ALL_SLOTS.reduce(
  (acc, slot) => {
    acc[slot] = {};
    return acc;
  },
  {}
);
var idCounter = 0;
function generateId() {
  return `slot-content-${++idCounter}-${Date.now()}`;
}
function aggregateSlot(sources) {
  if (!sources) return null;
  const entries = Object.entries(sources);
  if (entries.length === 0) return null;
  if (entries.length === 1) return entries[0][1];
  const children = entries.map(([, entry]) => ({
    type: entry.pattern,
    ...entry.props
  }));
  const stackId = `slot-content-stack-${entries.map(([k]) => k).join("-")}`;
  return {
    id: stackId,
    pattern: "stack",
    props: {
      direction: "vertical",
      gap: "lg",
      children
    },
    priority: 0,
    animation: "fade",
    sourceTrait: MULTI_SOURCE_STACK_TRAIT
  };
}
function useUISlotManager() {
  const [sources, setSources] = react.useState(DEFAULT_SOURCES);
  const subscribersRef = react.useRef(/* @__PURE__ */ new Set());
  const timersRef = react.useRef(/* @__PURE__ */ new Map());
  const traitIndexRef = react.useRef(/* @__PURE__ */ new Map());
  const traitSubscribersRef = react.useRef(/* @__PURE__ */ new Map());
  const slots = react.useMemo(() => {
    const out = { ...DEFAULT_SLOTS };
    for (const slot of ALL_SLOTS) {
      out[slot] = aggregateSlot(sources[slot]);
    }
    return out;
  }, [sources]);
  react.useEffect(() => {
    return () => {
      timersRef.current.forEach((timer) => clearTimeout(timer));
      timersRef.current.clear();
    };
  }, []);
  const notifySubscribers = react.useCallback((slot, content) => {
    subscribersRef.current.forEach((callback) => {
      try {
        callback(slot, content);
      } catch (error) {
        log.error("Subscriber error", { error: error instanceof Error ? error : String(error) });
      }
    });
  }, []);
  const notifyTraitSubscribers = react.useCallback(
    (traitName, content) => {
      const subs = traitSubscribersRef.current.get(traitName);
      if (!subs) return;
      subs.forEach((callback) => {
        try {
          callback(content);
        } catch (error) {
          log.error("Trait subscriber error", { traitName, error: error instanceof Error ? error : String(error) });
        }
      });
    },
    []
  );
  const indexTraitRender = react.useCallback(
    (traitName, content) => {
      traitIndexRef.current.set(traitName, content);
    },
    []
  );
  const unindexTrait = react.useCallback((traitName) => {
    traitIndexRef.current.delete(traitName);
  }, []);
  const render = react.useCallback(
    (config) => {
      const id = generateId();
      const sourceKey = config.sourceTrait ?? DEFAULT_SOURCE_KEY;
      const content = {
        id,
        pattern: config.pattern,
        props: config.props ?? {},
        priority: config.priority ?? 0,
        animation: config.animation ?? "fade",
        onDismiss: config.onDismiss,
        sourceTrait: config.sourceTrait
      };
      if (config.autoDismissMs && config.autoDismissMs > 0) {
        content.autoDismissAt = Date.now() + config.autoDismissMs;
        const timer = setTimeout(() => {
          setSources((prev) => {
            const slotSources = prev[config.target];
            if (slotSources && slotSources[sourceKey]?.id === id) {
              content.onDismiss?.();
              const next = { ...slotSources };
              delete next[sourceKey];
              const updated = { ...prev, [config.target]: next };
              notifySubscribers(config.target, aggregateSlot(next));
              return updated;
            }
            return prev;
          });
          timersRef.current.delete(id);
        }, config.autoDismissMs);
        timersRef.current.set(id, timer);
      }
      setSources((prev) => {
        const slotSources = prev[config.target] ?? {};
        const existing = slotSources[sourceKey];
        if (existing && existing.priority > content.priority) {
          log.warn("Slot already has higher priority content", {
            slot: config.target,
            sourceKey,
            existingPriority: existing.priority,
            newPriority: content.priority
          });
          return prev;
        }
        const nextSources = {
          ...slotSources,
          [sourceKey]: content
        };
        const nextAll = { ...prev, [config.target]: nextSources };
        if (content.sourceTrait) {
          indexTraitRender(content.sourceTrait, content);
          notifyTraitSubscribers(content.sourceTrait, content);
        }
        log.info("slot:written", {
          slot: config.target,
          sourceKey,
          sourceTrait: content.sourceTrait,
          patternType: content.pattern,
          priority: content.priority
        });
        notifySubscribers(config.target, aggregateSlot(nextSources));
        return nextAll;
      });
      return id;
    },
    [notifySubscribers, notifyTraitSubscribers, indexTraitRender]
  );
  const clear = react.useCallback(
    (slot) => {
      setSources((prev) => {
        const slotSources = prev[slot];
        if (!slotSources || Object.keys(slotSources).length === 0) {
          return prev;
        }
        for (const content of Object.values(slotSources)) {
          const timer = timersRef.current.get(content.id);
          if (timer) {
            clearTimeout(timer);
            timersRef.current.delete(content.id);
          }
          content.onDismiss?.();
          if (content.sourceTrait) {
            unindexTrait(content.sourceTrait);
            notifyTraitSubscribers(content.sourceTrait, null);
          }
        }
        notifySubscribers(slot, null);
        return { ...prev, [slot]: {} };
      });
    },
    [notifySubscribers, notifyTraitSubscribers, unindexTrait]
  );
  const clearBySource = react.useCallback(
    (slot, sourceTrait) => {
      const sourceKey = sourceTrait;
      setSources((prev) => {
        const slotSources = prev[slot];
        if (!slotSources || !(sourceKey in slotSources)) {
          log.debug("slot:clear-noop", { slot, sourceTrait, reason: !slotSources ? "no-slot" : "no-source" });
          return prev;
        }
        const content = slotSources[sourceKey];
        const timer = timersRef.current.get(content.id);
        if (timer) {
          clearTimeout(timer);
          timersRef.current.delete(content.id);
        }
        content.onDismiss?.();
        if (content.sourceTrait) {
          unindexTrait(content.sourceTrait);
          notifyTraitSubscribers(content.sourceTrait, null);
        }
        const nextSources = { ...slotSources };
        delete nextSources[sourceKey];
        log.info("slot:cleared", { slot, sourceTrait, lastPatternType: content.pattern });
        notifySubscribers(slot, aggregateSlot(nextSources));
        return { ...prev, [slot]: nextSources };
      });
    },
    [notifySubscribers, notifyTraitSubscribers, unindexTrait]
  );
  const clearById = react.useCallback(
    (id) => {
      setSources((prev) => {
        for (const slot of ALL_SLOTS) {
          const slotSources = prev[slot];
          if (!slotSources) continue;
          const matchKey = Object.keys(slotSources).find(
            (k) => slotSources[k].id === id
          );
          if (!matchKey) continue;
          const content = slotSources[matchKey];
          const timer = timersRef.current.get(id);
          if (timer) {
            clearTimeout(timer);
            timersRef.current.delete(id);
          }
          content.onDismiss?.();
          if (content.sourceTrait) {
            unindexTrait(content.sourceTrait);
            notifyTraitSubscribers(content.sourceTrait, null);
          }
          const nextSources = { ...slotSources };
          delete nextSources[matchKey];
          notifySubscribers(slot, aggregateSlot(nextSources));
          return { ...prev, [slot]: nextSources };
        }
        return prev;
      });
    },
    [notifySubscribers, notifyTraitSubscribers, unindexTrait]
  );
  const clearAll = react.useCallback(() => {
    timersRef.current.forEach((timer) => clearTimeout(timer));
    timersRef.current.clear();
    setSources((prev) => {
      for (const slot of ALL_SLOTS) {
        const slotSources = prev[slot];
        if (!slotSources) continue;
        for (const content of Object.values(slotSources)) {
          content.onDismiss?.();
          if (content.sourceTrait) {
            notifyTraitSubscribers(content.sourceTrait, null);
          }
        }
        notifySubscribers(slot, null);
      }
      return DEFAULT_SOURCES;
    });
    traitIndexRef.current.clear();
  }, [notifySubscribers, notifyTraitSubscribers]);
  const subscribe = react.useCallback((callback) => {
    subscribersRef.current.add(callback);
    return () => {
      subscribersRef.current.delete(callback);
    };
  }, []);
  const hasContent = react.useCallback(
    (slot) => slots[slot] !== null,
    [slots]
  );
  const getContent = react.useCallback(
    (slot) => slots[slot],
    [slots]
  );
  const getTraitContent = react.useCallback(
    (traitName) => traitIndexRef.current.get(traitName) ?? null,
    []
  );
  const subscribeTrait = react.useCallback(
    (traitName, callback) => {
      let set = traitSubscribersRef.current.get(traitName);
      if (!set) {
        set = /* @__PURE__ */ new Set();
        traitSubscribersRef.current.set(traitName, set);
      }
      set.add(callback);
      return () => {
        const s = traitSubscribersRef.current.get(traitName);
        if (!s) return;
        s.delete(callback);
        if (s.size === 0) {
          traitSubscribersRef.current.delete(traitName);
        }
      };
    },
    []
  );
  const updateTraitContent = react.useCallback(
    (traitName, content) => {
      const id = generateId();
      const fullContent = { ...content, id, sourceTrait: traitName };
      indexTraitRender(traitName, fullContent);
      notifyTraitSubscribers(traitName, fullContent);
      return id;
    },
    [indexTraitRender, notifyTraitSubscribers]
  );
  return {
    slots,
    render,
    clear,
    clearBySource,
    clearById,
    clearAll,
    subscribe,
    hasContent,
    getContent,
    getTraitContent,
    subscribeTrait,
    updateTraitContent
  };
}
var UISlotContext = react.createContext(null);
function UISlotProvider({ children }) {
  const slotManager = useUISlotManager();
  const contextValue = react.useMemo(() => slotManager, [slotManager]);
  return /* @__PURE__ */ jsxRuntime.jsx(UISlotContext.Provider, { value: contextValue, children });
}
function useUISlots() {
  const context = react.useContext(UISlotContext);
  if (!context) {
    throw new Error(
      "useUISlots must be used within a UISlotProvider. Make sure your component tree is wrapped with <UISlotProvider>."
    );
  }
  return context;
}
function useSlotContent(slot) {
  const { getContent } = useUISlots();
  return getContent(slot);
}
function useSlotHasContent(slot) {
  const { hasContent } = useUISlots();
  return hasContent(slot);
}
var log2 = logger.createLogger("almadar:ui:theme");
var BUILT_IN_THEMES = [
  {
    name: "wireframe",
    displayName: "Wireframe",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "minimalist",
    displayName: "Minimalist",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "almadar",
    displayName: "Almadar",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "trait-wars",
    displayName: "Trait Wars",
    hasLightMode: false,
    hasDarkMode: true
  },
  // Extended themes
  {
    name: "ocean",
    displayName: "Ocean",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "forest",
    displayName: "Forest",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "sunset",
    displayName: "Sunset",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "lavender",
    displayName: "Lavender",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "rose",
    displayName: "Rose",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "slate",
    displayName: "Slate",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "ember",
    displayName: "Ember",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "midnight",
    displayName: "Midnight",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "sand",
    displayName: "Sand",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "neon",
    displayName: "Neon",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "arctic",
    displayName: "Arctic",
    hasLightMode: true,
    hasDarkMode: true
  },
  {
    name: "copper",
    displayName: "Copper",
    hasLightMode: true,
    hasDarkMode: true
  }
];
var ThemeContext = react.createContext(void 0);
var THEME_STORAGE_KEY = "theme";
var MODE_STORAGE_KEY = "theme-mode";
function getSystemMode() {
  if (typeof window === "undefined") return "light";
  return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function resolveMode(mode) {
  if (mode === "system") {
    return getSystemMode();
  }
  return mode;
}
var ThemeProvider = ({
  children,
  themes = [],
  defaultTheme = "wireframe",
  defaultMode = "system",
  targetRef
}) => {
  const availableThemes = react.useMemo(() => {
    const themeMap = /* @__PURE__ */ new Map();
    BUILT_IN_THEMES.forEach((t) => themeMap.set(t.name, t));
    themes.forEach((t) => themeMap.set(t.name, t));
    return Array.from(themeMap.values());
  }, [themes]);
  const isScoped = !!targetRef;
  const [theme, setThemeState] = react.useState(() => {
    if (isScoped || typeof window === "undefined") return defaultTheme;
    const stored = localStorage.getItem(THEME_STORAGE_KEY);
    const validThemes = [
      ...BUILT_IN_THEMES.map((t) => t.name),
      ...themes.map((t) => t.name)
    ];
    if (stored && validThemes.includes(stored)) {
      return stored;
    }
    return defaultTheme;
  });
  const [mode, setModeState] = react.useState(() => {
    if (isScoped || typeof window === "undefined") return defaultMode;
    const stored = localStorage.getItem(MODE_STORAGE_KEY);
    if (stored === "light" || stored === "dark" || stored === "system") {
      return stored;
    }
    return defaultMode;
  });
  const [resolvedMode, setResolvedMode] = react.useState(
    () => resolveMode(mode)
  );
  const appliedTheme = react.useMemo(
    () => `${theme}-${resolvedMode}`,
    [theme, resolvedMode]
  );
  react.useEffect(() => {
    const updateResolved = () => {
      setResolvedMode(resolveMode(mode));
    };
    updateResolved();
    if (mode === "system") {
      const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
      const handleChange = () => updateResolved();
      mediaQuery.addEventListener("change", handleChange);
      return () => mediaQuery.removeEventListener("change", handleChange);
    }
    return void 0;
  }, [mode]);
  react.useEffect(() => {
    if (isScoped) {
      if (targetRef?.current) {
        targetRef.current.setAttribute("data-theme", appliedTheme);
        targetRef.current.classList.remove("light", "dark");
        targetRef.current.classList.add(resolvedMode);
      }
      return;
    }
    const root = document.documentElement;
    root.setAttribute("data-theme", appliedTheme);
    root.classList.remove("light", "dark");
    root.classList.add(resolvedMode);
  }, [appliedTheme, resolvedMode, targetRef, isScoped]);
  const setTheme = react.useCallback(
    (newTheme) => {
      const validTheme = availableThemes.find((t) => t.name === newTheme);
      if (validTheme) {
        setThemeState(newTheme);
        if (!isScoped && typeof window !== "undefined") {
          localStorage.setItem(THEME_STORAGE_KEY, newTheme);
        }
      } else {
        log2.warn("Theme not found", {
          theme: newTheme,
          available: availableThemes.map((t) => t.name)
        });
      }
    },
    [availableThemes]
  );
  const setMode = react.useCallback((newMode) => {
    setModeState(newMode);
    if (!isScoped && typeof window !== "undefined") {
      localStorage.setItem(MODE_STORAGE_KEY, newMode);
    }
  }, []);
  const toggleMode = react.useCallback(() => {
    const newMode = resolvedMode === "dark" ? "light" : "dark";
    setMode(newMode);
  }, [resolvedMode, setMode]);
  const contextValue = react.useMemo(
    () => ({
      theme,
      mode,
      resolvedMode,
      setTheme,
      setMode,
      toggleMode,
      availableThemes,
      appliedTheme
    }),
    [
      theme,
      mode,
      resolvedMode,
      setTheme,
      setMode,
      toggleMode,
      availableThemes,
      appliedTheme
    ]
  );
  return /* @__PURE__ */ jsxRuntime.jsx(ThemeContext.Provider, { value: contextValue, children });
};
function useTheme() {
  const context = react.useContext(ThemeContext);
  if (context === void 0) {
    return {
      theme: "wireframe",
      mode: "light",
      resolvedMode: "light",
      setTheme: () => {
      },
      setMode: () => {
      },
      toggleMode: () => {
      },
      availableThemes: BUILT_IN_THEMES,
      appliedTheme: "wireframe-light"
    };
  }
  return context;
}
var ThemeContext_default = ThemeContext;

// context/themeTokens.ts
function themeTokensToCssVars(tokens, mode = "light", darkVariant) {
  const vars = {};
  const isDark = mode === "dark";
  const pickColors = isDark && darkVariant?.colors ? darkVariant.colors : tokens.colors;
  if (pickColors) {
    for (const [key, value] of Object.entries(pickColors)) {
      vars[`--color-${key}`] = value;
    }
    if (isDark && darkVariant?.colors && tokens.colors) {
      for (const [key, value] of Object.entries(tokens.colors)) {
        const varName = `--color-${key}`;
        if (!(varName in vars)) vars[varName] = value;
      }
    }
  }
  const pickRadii = isDark && darkVariant?.radii ? darkVariant.radii : tokens.radii;
  if (pickRadii) {
    for (const [key, value] of Object.entries(pickRadii)) {
      vars[`--radius-${key}`] = value;
    }
  }
  const pickSpacing = isDark && darkVariant?.spacing ? darkVariant.spacing : tokens.spacing;
  if (pickSpacing) {
    for (const [key, value] of Object.entries(pickSpacing)) {
      vars[`--space-${key}`] = value;
    }
  }
  const pickTypography = isDark && darkVariant?.typography ? darkVariant.typography : tokens.typography;
  if (pickTypography) {
    for (const [key, value] of Object.entries(pickTypography)) {
      vars[`--${key}`] = value;
    }
  }
  const pickShadows = isDark && darkVariant?.shadows ? darkVariant.shadows : tokens.shadows;
  if (pickShadows) {
    for (const [key, value] of Object.entries(pickShadows)) {
      vars[`--shadow-${key}`] = value;
    }
  }
  return vars;
}
function resolveThemeForRuntime(theme) {
  if (theme === void 0) return void 0;
  if (typeof theme === "string") return void 0;
  return theme;
}
function OrbitalThemeProvider({ theme, children }) {
  const resolved = resolveThemeForRuntime(theme);
  const { resolvedMode } = useTheme();
  if (!resolved) {
    return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
  }
  const vars = themeTokensToCssVars(resolved.tokens, resolvedMode, resolved.variants?.dark);
  return /* @__PURE__ */ jsxRuntime.jsx(
    "div",
    {
      "data-orbital-theme": resolved.name,
      style: { display: "contents", ...vars },
      children
    }
  );
}
OrbitalThemeProvider.displayName = "OrbitalThemeProvider";

// context/DesignThemeContext.tsx
var DesignThemeProvider = ThemeProvider;
function useDesignTheme() {
  const { theme, setTheme, availableThemes } = useTheme();
  return {
    designTheme: theme,
    setDesignTheme: setTheme,
    availableThemes: availableThemes.map((t) => t.name)
  };
}
var CurrentPagePathContext = react.createContext(void 0);
var CurrentPagePathProvider = ({
  value,
  children
}) => /* @__PURE__ */ jsxRuntime.jsx(CurrentPagePathContext.Provider, { value, children });
CurrentPagePathProvider.displayName = "CurrentPagePathProvider";
var useCurrentPagePath = () => react.useContext(CurrentPagePathContext);
var ANONYMOUS_USER = {
  id: "anonymous",
  role: "anonymous",
  permissions: []
};
var UserContext = react.createContext(null);
function UserProvider({
  user = null,
  children
}) {
  const hasRole = react.useCallback(
    (role) => {
      if (!user) return role === "anonymous";
      return user.role === role;
    },
    [user]
  );
  const hasPermission = react.useCallback(
    (permission) => {
      if (!user) return false;
      return user.permissions?.includes(permission) ?? false;
    },
    [user]
  );
  const hasAnyRole = react.useCallback(
    (roles) => {
      if (!user) return roles.includes("anonymous");
      return user.role ? roles.includes(user.role) : false;
    },
    [user]
  );
  const hasAllPermissions = react.useCallback(
    (permissions) => {
      if (!user || !user.permissions) return false;
      return permissions.every((p) => user.permissions?.includes(p));
    },
    [user]
  );
  const getUserField = react.useCallback(
    (path) => {
      const userData = user ?? ANONYMOUS_USER;
      const parts = path.split(".");
      let value = userData;
      for (const segment of parts) {
        if (value === null || value === void 0) {
          return void 0;
        }
        if (typeof value === "object") {
          value = value[segment];
        } else {
          return void 0;
        }
      }
      return value;
    },
    [user]
  );
  const contextValue = react.useMemo(
    () => ({
      user,
      isLoggedIn: user !== null,
      hasRole,
      hasPermission,
      hasAnyRole,
      hasAllPermissions,
      getUserField
    }),
    [user, hasRole, hasPermission, hasAnyRole, hasAllPermissions, getUserField]
  );
  return /* @__PURE__ */ jsxRuntime.jsx(UserContext.Provider, { value: contextValue, children });
}
function useUser() {
  const context = react.useContext(UserContext);
  if (!context) {
    return {
      user: null,
      isLoggedIn: false,
      hasRole: (role) => role === "anonymous",
      hasPermission: () => false,
      hasAnyRole: (roles) => roles.includes("anonymous"),
      hasAllPermissions: () => false,
      getUserField: () => void 0
    };
  }
  return context;
}
function useHasRole(role) {
  const { hasRole } = useUser();
  return hasRole(role);
}
function useHasPermission(permission) {
  const { hasPermission } = useUser();
  return hasPermission(permission);
}
function useUserForEvaluation() {
  const { user, isLoggedIn } = useUser();
  return isLoggedIn && user ? user : void 0;
}

exports.ANONYMOUS_USER = ANONYMOUS_USER;
exports.BUILT_IN_THEMES = BUILT_IN_THEMES;
exports.CurrentPagePathContext = CurrentPagePathContext;
exports.CurrentPagePathProvider = CurrentPagePathProvider;
exports.DesignThemeProvider = DesignThemeProvider;
exports.OrbitalThemeProvider = OrbitalThemeProvider;
exports.ThemeContext = ThemeContext_default;
exports.ThemeProvider = ThemeProvider;
exports.UISlotContext = UISlotContext;
exports.UISlotProvider = UISlotProvider;
exports.UserContext = UserContext;
exports.UserProvider = UserProvider;
exports.resolveThemeForRuntime = resolveThemeForRuntime;
exports.themeTokensToCssVars = themeTokensToCssVars;
exports.useCurrentPagePath = useCurrentPagePath;
exports.useDesignTheme = useDesignTheme;
exports.useHasPermission = useHasPermission;
exports.useHasRole = useHasRole;
exports.useSlotContent = useSlotContent;
exports.useSlotHasContent = useSlotHasContent;
exports.useTheme = useTheme;
exports.useUISlots = useUISlots;
exports.useUser = useUser;
exports.useUserForEvaluation = useUserForEvaluation;
