import { flash } from "@open-mercato/ui/backend/FlashMessages";
import {
  getNotificationHandlerEntries
} from "@open-mercato/shared/lib/notifications/handler-registry";
import { hasAllFeatures } from "@open-mercato/shared/security/features";
const DEFAULT_PRIORITY = 50;
const MAX_TRACKED_IDS = 500;
const RETAINED_IDS_AFTER_PRUNE = 200;
const POPUP_EVENT_NAME = "om:notifications:popup";
function toFlashKind(value) {
  if (value === "success") return "success";
  if (value === "warning") return "warning";
  if (value === "error") return "error";
  return "info";
}
function matchesType(pattern, type) {
  const patterns = Array.isArray(pattern) ? pattern : [pattern];
  return patterns.some((current) => {
    if (current === "*") return true;
    if (current.endsWith(".*")) return type.startsWith(current.slice(0, -1));
    return current === type;
  });
}
function matchesFeatures(required, current) {
  return hasAllFeatures(current, required);
}
function buildHandlerContext(runtime) {
  return {
    userId: runtime.userId,
    features: runtime.features,
    currentPath: runtime.currentPath,
    toast(options) {
      const text = options.body ? `${options.title}: ${options.body}` : options.title;
      flash(text, toFlashKind(options.severity));
    },
    popup(options) {
      if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") {
        window.dispatchEvent(new CustomEvent(POPUP_EVENT_NAME, { detail: options }));
      }
      const text = options.body ? `${options.title}: ${options.body}` : options.title;
      flash(text, toFlashKind(options.severity));
    },
    emitEvent(eventName, detail) {
      if (typeof window === "undefined" || typeof CustomEvent === "undefined") return;
      window.dispatchEvent(new CustomEvent(eventName, { detail }));
    },
    refreshNotifications: runtime.refreshNotifications,
    navigate(href) {
      runtime.navigate(href);
    },
    markAsRead: runtime.markAsRead,
    dismiss: runtime.dismiss
  };
}
class NotificationDispatcher {
  constructor() {
    this.handledIds = /* @__PURE__ */ new Set();
    this.handledOrder = [];
    this.listeners = /* @__PURE__ */ new Map();
    this.listenerIdSeq = 1;
    this.debounceByHandler = /* @__PURE__ */ new Map();
  }
  dispatch(notifications, runtime) {
    const entries = getNotificationHandlerEntries();
    if (notifications.length === 0 && this.listeners.size === 0) return;
    const context = buildHandlerContext(runtime);
    const sortedEntries = entries.slice().sort((a, b) => (b.handler.priority ?? DEFAULT_PRIORITY) - (a.handler.priority ?? DEFAULT_PRIORITY));
    for (const notification of notifications) {
      if (this.handledIds.has(notification.id)) continue;
      let handlerRan = false;
      let hadFeatureBlockedHandler = false;
      for (const entry of sortedEntries) {
        const handler = entry.handler;
        if (!matchesType(handler.notificationType, notification.type)) continue;
        if (!matchesFeatures(handler.features, runtime.features)) {
          hadFeatureBlockedHandler = true;
          continue;
        }
        if (this.shouldDebounce(handler, notification.id)) continue;
        handlerRan = true;
        try {
          void Promise.resolve(handler.handle(notification, context));
        } catch (error) {
          console.error(`[notifications] handler failed: ${handler.id}`, error);
        }
      }
      for (const listener of this.listeners.values()) {
        if (!matchesType(listener.pattern, notification.type)) continue;
        handlerRan = true;
        try {
          listener.effect(notification);
        } catch (error) {
          console.error("[notifications] effect listener failed", error);
        }
      }
      if (handlerRan || !hadFeatureBlockedHandler) {
        this.markHandled(notification.id);
      }
    }
  }
  subscribe(pattern, effect) {
    const id = this.listenerIdSeq++;
    this.listeners.set(id, { id, pattern, effect });
    return () => {
      this.listeners.delete(id);
    };
  }
  resetForTests() {
    this.handledIds.clear();
    this.handledOrder = [];
    this.listeners.clear();
    this.listenerIdSeq = 1;
    this.debounceByHandler.clear();
  }
  shouldDebounce(handler, notificationId) {
    if (!handler.debounceMs || handler.debounceMs <= 0) return false;
    const key = `${handler.id}:${notificationId}`;
    const now = Date.now();
    const previous = this.debounceByHandler.get(key);
    if (typeof previous === "number" && now - previous < handler.debounceMs) {
      return true;
    }
    this.debounceByHandler.set(key, now);
    return false;
  }
  markHandled(notificationId) {
    this.handledIds.add(notificationId);
    this.handledOrder.push(notificationId);
    if (this.handledOrder.length <= MAX_TRACKED_IDS) return;
    const removeCount = this.handledOrder.length - RETAINED_IDS_AFTER_PRUNE;
    const removed = this.handledOrder.splice(0, removeCount);
    for (const id of removed) {
      this.handledIds.delete(id);
    }
  }
}
const dispatcher = new NotificationDispatcher();
function dispatchNotificationHandlers(notifications, runtime) {
  dispatcher.dispatch(notifications, runtime);
}
function subscribeNotificationEffects(pattern, effect) {
  return dispatcher.subscribe(pattern, effect);
}
function getRequiredNotificationHandlerFeatures() {
  const required = /* @__PURE__ */ new Set();
  const entries = getNotificationHandlerEntries();
  for (const entry of entries) {
    for (const feature of entry.handler.features ?? []) {
      if (feature && feature.trim().length > 0) {
        required.add(feature);
      }
    }
  }
  return Array.from(required);
}
function __resetNotificationDispatcherForTests() {
  dispatcher.resetForTests();
}
export {
  __resetNotificationDispatcherForTests,
  dispatchNotificationHandlers,
  getRequiredNotificationHandlerFeatures,
  subscribeNotificationEffects
};
//# sourceMappingURL=NotificationDispatcher.js.map
