import { matchWildcardPattern } from "@open-mercato/shared/lib/patterns/wildcard";
import { createLogger } from "../../lib/logger/index.js";
import {
  applyInjectionWidgetOverridesToEntries,
  applyInjectionWidgetOverridesToTables
} from "../overrides.js";
const logger = createLogger("widgets").child({ component: "injection-loader" });
let _coreInjectionWidgetEntries = null;
let _coreInjectionTables = null;
let _enabledModuleIds = null;
let _injectionRegistryVersion = 0;
const GLOBAL_INJECTION_WIDGETS_KEY = "__openMercatoCoreInjectionWidgetEntries__";
const GLOBAL_INJECTION_TABLES_KEY = "__openMercatoCoreInjectionTables__";
const GLOBAL_ENABLED_MODULE_IDS_KEY = "__openMercatoEnabledModuleIds__";
const GLOBAL_INJECTION_REGISTRY_VERSION_KEY = "__openMercatoCoreInjectionRegistryVersion__";
const INJECTION_REGISTRY_CHANGED_EVENT = "__openMercatoInjectionRegistryChanged__";
function readGlobalInjectionWidgets() {
  try {
    const value = globalThis[GLOBAL_INJECTION_WIDGETS_KEY];
    return Array.isArray(value) ? value : null;
  } catch {
    return null;
  }
}
function writeGlobalInjectionWidgets(entries) {
  try {
    ;
    globalThis[GLOBAL_INJECTION_WIDGETS_KEY] = entries;
  } catch {
  }
}
function readGlobalEnabledModuleIds() {
  try {
    const value = globalThis[GLOBAL_ENABLED_MODULE_IDS_KEY];
    if (value instanceof Set) return value;
    return null;
  } catch {
    return null;
  }
}
function writeGlobalEnabledModuleIds(ids) {
  try {
    ;
    globalThis[GLOBAL_ENABLED_MODULE_IDS_KEY] = ids;
  } catch {
  }
}
function readGlobalInjectionTables() {
  try {
    const value = globalThis[GLOBAL_INJECTION_TABLES_KEY];
    return Array.isArray(value) ? value : null;
  } catch {
    return null;
  }
}
function writeGlobalInjectionTables(tables) {
  try {
    ;
    globalThis[GLOBAL_INJECTION_TABLES_KEY] = tables;
  } catch {
  }
}
function readGlobalInjectionRegistryVersion() {
  try {
    const value = globalThis[GLOBAL_INJECTION_REGISTRY_VERSION_KEY];
    return typeof value === "number" ? value : null;
  } catch {
    return null;
  }
}
function writeGlobalInjectionRegistryVersion(version) {
  try {
    ;
    globalThis[GLOBAL_INJECTION_REGISTRY_VERSION_KEY] = version;
  } catch {
  }
}
function notifyInjectionRegistryChanged() {
  _injectionRegistryVersion += 1;
  writeGlobalInjectionRegistryVersion(_injectionRegistryVersion);
  invalidateInjectionWidgetCache();
  if (typeof window === "undefined") return;
  window.dispatchEvent(new CustomEvent(INJECTION_REGISTRY_CHANGED_EVENT, {
    detail: { version: _injectionRegistryVersion }
  }));
}
function registerCoreInjectionWidgets(entries) {
  if (_coreInjectionWidgetEntries !== null && process.env.NODE_ENV === "development") {
    logger.debug("Core injection widgets re-registered (this may occur during HMR)");
  }
  const finalEntries = applyInjectionWidgetOverridesToEntries(entries);
  _coreInjectionWidgetEntries = finalEntries;
  writeGlobalInjectionWidgets(finalEntries);
  notifyInjectionRegistryChanged();
}
function getCoreInjectionWidgets() {
  const globalEntries = readGlobalInjectionWidgets();
  if (globalEntries) return globalEntries;
  if (!_coreInjectionWidgetEntries) {
    if (typeof window !== "undefined") {
      return [];
    }
    throw new Error("[Bootstrap] Core injection widgets not registered. Call registerCoreInjectionWidgets() at bootstrap.");
  }
  return _coreInjectionWidgetEntries;
}
function registerCoreInjectionTables(tables) {
  if (_coreInjectionTables !== null && process.env.NODE_ENV === "development") {
    logger.debug("Core injection tables re-registered (this may occur during HMR)");
  }
  const finalTables = applyInjectionWidgetOverridesToTables(tables);
  _coreInjectionTables = finalTables;
  writeGlobalInjectionTables(finalTables);
  notifyInjectionRegistryChanged();
}
function registerEnabledModuleIds(moduleIds) {
  const next = /* @__PURE__ */ new Set();
  for (const moduleId of moduleIds) {
    if (typeof moduleId === "string" && moduleId.length > 0) next.add(moduleId);
  }
  if (_enabledModuleIds !== null && process.env.NODE_ENV === "development") {
    logger.debug("Enabled module IDs re-registered (this may occur during HMR)");
  }
  _enabledModuleIds = next;
  writeGlobalEnabledModuleIds(next);
  notifyInjectionRegistryChanged();
}
function getEnabledModuleIds() {
  return readGlobalEnabledModuleIds() ?? _enabledModuleIds;
}
function getInjectionRegistryVersion() {
  const globalVersion = readGlobalInjectionRegistryVersion();
  if (globalVersion !== null) return globalVersion;
  return _injectionRegistryVersion;
}
const injectionRegistryChangeSubscribers = /* @__PURE__ */ new Set();
let injectionRegistryDomListenerAttached = false;
function dispatchInjectionRegistryChangeToSubscribers() {
  for (const subscriber of Array.from(injectionRegistryChangeSubscribers)) {
    subscriber();
  }
}
function subscribeToInjectionRegistryChanges(listener) {
  if (typeof window === "undefined") {
    return () => {
    };
  }
  injectionRegistryChangeSubscribers.add(listener);
  if (!injectionRegistryDomListenerAttached) {
    window.addEventListener(INJECTION_REGISTRY_CHANGED_EVENT, dispatchInjectionRegistryChangeToSubscribers);
    injectionRegistryDomListenerAttached = true;
  }
  return () => {
    injectionRegistryChangeSubscribers.delete(listener);
    if (injectionRegistryChangeSubscribers.size === 0 && injectionRegistryDomListenerAttached) {
      window.removeEventListener(INJECTION_REGISTRY_CHANGED_EVENT, dispatchInjectionRegistryChangeToSubscribers);
      injectionRegistryDomListenerAttached = false;
    }
  };
}
function getCoreInjectionTables() {
  const globalTables = readGlobalInjectionTables();
  if (globalTables) return globalTables;
  if (!_coreInjectionTables) {
    if (typeof window !== "undefined") {
      return [];
    }
    throw new Error("[Bootstrap] Core injection tables not registered. Call registerCoreInjectionTables() at bootstrap.");
  }
  return _coreInjectionTables;
}
let widgetEntriesPromise = null;
let injectionTablePromise = null;
let widgetLookupIndexPromise = null;
function isInjectionSlotObject(value) {
  return typeof value === "object" && value !== null && "widgetId" in value;
}
function invalidateInjectionWidgetCache() {
  widgetEntriesPromise = null;
  injectionTablePromise = null;
  widgetLookupIndexPromise = null;
  widgetCache.clear();
  warnedRequiredModuleSkips.clear();
}
async function loadWidgetEntries() {
  if (!widgetEntriesPromise) {
    const promise = Promise.resolve().then(
      () => getCoreInjectionWidgets().map((entry) => ({
        ...entry,
        moduleId: entry.moduleId || "unknown"
      }))
    );
    widgetEntriesPromise = promise.catch((err) => {
      if (widgetEntriesPromise === promise) {
        widgetEntriesPromise = null;
      }
      throw err;
    });
  }
  return widgetEntriesPromise;
}
async function loadInjectionTable() {
  if (!injectionTablePromise) {
    const promise = Promise.resolve().then(() => {
      const list = getCoreInjectionTables();
      const table = /* @__PURE__ */ new Map();
      for (const entry of list) {
        const injectionTable = entry.table ?? {};
        for (const [spotId, widgetIds] of Object.entries(injectionTable)) {
          const widgets = Array.isArray(widgetIds) ? widgetIds : [widgetIds];
          const existing = table.get(spotId) ?? [];
          for (const widgetEntry of widgets) {
            if (typeof widgetEntry === "string") {
              existing.push({ widgetId: widgetEntry, moduleId: entry.moduleId, priority: 0 });
              continue;
            }
            if (isInjectionSlotObject(widgetEntry)) {
              const { widgetId, priority = 0, ...placement } = widgetEntry;
              existing.push({
                widgetId,
                moduleId: entry.moduleId,
                priority: typeof priority === "number" ? priority : 0,
                placement
              });
              continue;
            }
          }
          table.set(spotId, existing);
        }
      }
      for (const [spotId, widgets] of table.entries()) {
        table.set(spotId, widgets.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)));
      }
      return table;
    });
    injectionTablePromise = promise.catch((err) => {
      if (injectionTablePromise === promise) {
        injectionTablePromise = null;
      }
      throw err;
    });
  }
  return injectionTablePromise;
}
const widgetCache = /* @__PURE__ */ new Map();
function isDataWidgetModule(widget) {
  const keys = [
    "columns",
    "rowActions",
    "bulkActions",
    "filters",
    "fields",
    "steps",
    "badge",
    "menuItems"
  ];
  return keys.some((key) => key in widget);
}
function ensureValidInjectionModule(mod, key, moduleId) {
  if (!mod || typeof mod !== "object") {
    throw new Error(`Invalid injection widget module "${key}" from "${moduleId}" (expected object export)`);
  }
  const widget = mod.default ?? mod;
  if (!widget || typeof widget !== "object") {
    throw new Error(`Invalid injection widget export "${key}" from "${moduleId}" (missing default export)`);
  }
  if (!("metadata" in widget) || !widget.metadata || typeof widget.metadata !== "object") {
    throw new Error(`Injection widget "${key}" from "${moduleId}" is missing metadata`);
  }
  const metadata = widget.metadata;
  if (typeof metadata.id !== "string" || metadata.id.length === 0) {
    throw new Error(`Injection widget "${key}" from "${moduleId}" metadata.id must be a non-empty string`);
  }
  const normalized = {
    ...widget,
    metadata
  };
  if ("Widget" in normalized && typeof normalized.Widget === "function") {
    if (typeof metadata.title !== "string" || metadata.title.length === 0) {
      throw new Error(`Injection widget "${metadata.id}" from "${moduleId}" must have a title`);
    }
    return normalized;
  }
  if (!isDataWidgetModule(normalized)) {
    throw new Error(
      `Injection widget "${metadata.id}" from "${moduleId}" must export either Widget component or a declarative data payload`
    );
  }
  return normalized;
}
function isLoadedInjectionWidget(module) {
  return "Widget" in module && typeof module.Widget === "function";
}
function isLoadedInjectionDataWidget(module) {
  return !isLoadedInjectionWidget(module);
}
async function loadEntry(entry) {
  if (!widgetCache.has(entry.key)) {
    const promise = Promise.resolve().then(() => entry.loader()).then((mod) => ensureValidInjectionModule(mod, entry.key, entry.moduleId));
    widgetCache.set(entry.key, promise);
  }
  return widgetCache.get(entry.key);
}
async function loadWidgetLookupIndex() {
  const version = getInjectionRegistryVersion();
  if (!widgetLookupIndexPromise || widgetLookupIndexPromise.version !== version) {
    const promise = Promise.resolve().then(async () => {
      const widgetEntries = await loadWidgetEntries();
      const settled = await Promise.allSettled(widgetEntries.map((entry) => loadEntry(entry)));
      const widgetsById = /* @__PURE__ */ new Map();
      const dataWidgetsById = /* @__PURE__ */ new Map();
      settled.forEach((result, index) => {
        if (result.status !== "fulfilled") return;
        const entry = widgetEntries[index];
        const module = result.value;
        if (isLoadedInjectionWidget(module)) {
          if (!widgetsById.has(module.metadata.id)) {
            widgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key });
          }
          return;
        }
        if (!dataWidgetsById.has(module.metadata.id)) {
          dataWidgetsById.set(module.metadata.id, { ...module, moduleId: entry.moduleId, key: entry.key });
        }
      });
      return { widgetsById, dataWidgetsById };
    });
    widgetLookupIndexPromise = { version, promise };
  }
  return widgetLookupIndexPromise.promise;
}
function applyRequiredModuleGate(widget, enabledModuleIds) {
  const missing = widgetMissingRequiredModules(widget.metadata, enabledModuleIds);
  if (missing.length > 0) {
    warnSkippedWidget(widget.metadata.id, missing);
    return null;
  }
  return widget;
}
async function tryLoadHintedWidgetById(widgetId, isExpectedKind, enabledModuleIds) {
  const widgetEntries = await loadWidgetEntries();
  const entry = widgetEntries.find((candidate) => candidate.widgetId === widgetId);
  if (!entry) return { resolved: false };
  const module = await loadEntry(entry).catch(() => null);
  if (!module || module.metadata.id !== widgetId || !isExpectedKind(module)) {
    return { resolved: false };
  }
  const widget = { ...module, moduleId: entry.moduleId, key: entry.key };
  return { resolved: true, widget: applyRequiredModuleGate(widget, enabledModuleIds) };
}
function getEnabledModuleIdsForInjection() {
  const explicit = readGlobalEnabledModuleIds() ?? _enabledModuleIds;
  if (explicit) return explicit;
  const enabled = /* @__PURE__ */ new Set();
  const tables = readGlobalInjectionTables() ?? _coreInjectionTables ?? [];
  for (const entry of tables) {
    if (entry?.moduleId) enabled.add(entry.moduleId);
  }
  const entries = readGlobalInjectionWidgets() ?? _coreInjectionWidgetEntries ?? [];
  for (const entry of entries) {
    if (entry?.moduleId) enabled.add(entry.moduleId);
  }
  return enabled;
}
function widgetMissingRequiredModules(metadata, enabledModuleIds) {
  const required = metadata.requiredModules;
  if (!Array.isArray(required) || required.length === 0) return [];
  const missing = [];
  for (const moduleId of required) {
    if (typeof moduleId !== "string" || moduleId.length === 0) continue;
    if (!enabledModuleIds.has(moduleId)) missing.push(moduleId);
  }
  return missing;
}
const warnedRequiredModuleSkips = /* @__PURE__ */ new Set();
function warnSkippedWidget(metadataId, missingModules) {
  const key = `${metadataId}:${missingModules.join(",")}`;
  if (warnedRequiredModuleSkips.has(key)) return;
  warnedRequiredModuleSkips.add(key);
  if (process.env.NODE_ENV === "development") {
    logger.debug("Skipping widget \u2014 required module(s) not enabled", { metadataId, missingModules });
  }
}
async function getResolvedEntriesForSpot(spotId) {
  const table = await loadInjectionTable();
  const exactEntries = table.get(spotId) ?? [];
  const wildcardEntries = [];
  for (const [candidateSpotId, candidateEntries] of table.entries()) {
    if (candidateSpotId === spotId) continue;
    if (!candidateSpotId.includes("*")) continue;
    if (!matchWildcardPattern(spotId, candidateSpotId)) continue;
    wildcardEntries.push(...candidateEntries);
  }
  const dedupedEntries = /* @__PURE__ */ new Map();
  for (const entry of [...exactEntries, ...wildcardEntries]) {
    const cacheKey = `${entry.moduleId}:${entry.widgetId}`;
    const previous = dedupedEntries.get(cacheKey);
    if (!previous || (entry.priority ?? 0) > (previous.priority ?? 0)) {
      dedupedEntries.set(cacheKey, entry);
    }
  }
  return Array.from(dedupedEntries.values()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
}
async function loadAllInjectionWidgets() {
  const widgetEntries = await loadWidgetEntries();
  const enabledModuleIds = getEnabledModuleIdsForInjection();
  const loaded = await Promise.all(
    widgetEntries.map(async (entry) => {
      const module = await loadEntry(entry);
      if (!isLoadedInjectionWidget(module)) return null;
      const missing = widgetMissingRequiredModules(module.metadata, enabledModuleIds);
      if (missing.length > 0) {
        warnSkippedWidget(module.metadata.id, missing);
        return null;
      }
      return { ...module, moduleId: entry.moduleId, key: entry.key };
    })
  );
  const byId = /* @__PURE__ */ new Map();
  for (const widget of loaded) {
    if (!widget) continue;
    if (!byId.has(widget.metadata.id)) {
      byId.set(widget.metadata.id, widget);
    }
  }
  return Array.from(byId.values());
}
async function loadInjectionWidgetById(widgetId) {
  const enabledModuleIds = getEnabledModuleIdsForInjection();
  const hinted = await tryLoadHintedWidgetById(widgetId, isLoadedInjectionWidget, enabledModuleIds);
  if (hinted.resolved) return hinted.widget;
  const index = await loadWidgetLookupIndex();
  const widget = index.widgetsById.get(widgetId);
  return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null;
}
async function loadInjectionDataWidgetById(widgetId) {
  const enabledModuleIds = getEnabledModuleIdsForInjection();
  const hinted = await tryLoadHintedWidgetById(widgetId, isLoadedInjectionDataWidget, enabledModuleIds);
  if (hinted.resolved) return hinted.widget;
  const index = await loadWidgetLookupIndex();
  const widget = index.dataWidgetsById.get(widgetId);
  return widget ? applyRequiredModuleGate(widget, enabledModuleIds) : null;
}
async function loadInjectionWidgetsForSpot(spotId) {
  const entries = await getResolvedEntriesForSpot(spotId);
  const widgets = [];
  for (const { widgetId, placement, priority } of entries) {
    const widget = await loadInjectionWidgetById(widgetId);
    if (!widget) continue;
    const combinedPlacement = placement ? { ...placement, priority: typeof priority === "number" ? priority : 0 } : { priority: typeof priority === "number" ? priority : 0 };
    widgets.push({ ...widget, placement: combinedPlacement });
  }
  return widgets;
}
async function loadInjectionDataWidgetsForSpot(spotId) {
  const entries = await getResolvedEntriesForSpot(spotId);
  const widgets = [];
  for (const { widgetId, placement, priority } of entries) {
    const widget = await loadInjectionDataWidgetById(widgetId);
    if (!widget) continue;
    const combinedPlacement = placement ? { ...placement, priority: typeof priority === "number" ? priority : 0 } : { priority: typeof priority === "number" ? priority : 0 };
    widgets.push({ ...widget, placement: combinedPlacement });
  }
  return widgets;
}
export {
  getCoreInjectionTables,
  getCoreInjectionWidgets,
  getEnabledModuleIds,
  getInjectionRegistryVersion,
  invalidateInjectionWidgetCache,
  loadAllInjectionWidgets,
  loadInjectionDataWidgetById,
  loadInjectionDataWidgetsForSpot,
  loadInjectionWidgetById,
  loadInjectionWidgetsForSpot,
  registerCoreInjectionTables,
  registerCoreInjectionWidgets,
  registerEnabledModuleIds,
  subscribeToInjectionRegistryChanges
};
//# sourceMappingURL=injection-loader.js.map
