import { sql } from "kysely";
import { parseNumberWithDefault } from "@open-mercato/shared/lib/number";
function isSearchFilterOp(op) {
  return op === "like" || op === "ilike";
}
function hasSearchFilter(filters) {
  return filters.some((filter) => isSearchFilterOp(filter.op));
}
function orgScopeKey(scope) {
  if (!scope) return "none";
  return `${scope.includeNull ? "1" : "0"}:${[...scope.ids].sort((left, right) => left.localeCompare(right)).join(",")}`;
}
const PRESENCE_CACHE_DEFAULT_TTL_MS = 3e4;
const PRESENCE_CACHE_MAX_ENTRIES = 1e4;
const presenceCache = /* @__PURE__ */ new Map();
function resolvePresenceCacheTtlMs() {
  return parseNumberWithDefault(process.env.OM_SEARCH_TOKEN_PRESENCE_CACHE_MS, PRESENCE_CACHE_DEFAULT_TTL_MS, { integer: true, min: 0 });
}
function storePresence(key, value, ttlMs) {
  if (presenceCache.size >= PRESENCE_CACHE_MAX_ENTRIES) {
    const now = Date.now();
    for (const [entryKey, entry] of presenceCache) {
      if (entry.expiresAt <= now) presenceCache.delete(entryKey);
    }
    if (presenceCache.size >= PRESENCE_CACHE_MAX_ENTRIES) presenceCache.clear();
  }
  presenceCache.set(key, { value, expiresAt: Date.now() + ttlMs });
}
function clearSearchTokenPresenceCache() {
  presenceCache.clear();
}
function createSearchTokenAvailability(deps) {
  const tablePresence = /* @__PURE__ */ new Map();
  const tokenPresence = /* @__PURE__ */ new Map();
  const tableExists = (table) => {
    const cached = tablePresence.get(table);
    if (cached) return cached;
    const probe = (async () => {
      const row = await deps.getDb().selectFrom("information_schema.tables").select(sql`1`.as("one")).where("table_name", "=", table).limit(1).executeTakeFirst();
      return !!row;
    })();
    tablePresence.set(table, probe);
    probe.catch(() => tablePresence.delete(table));
    return probe;
  };
  const probeTokens = async (entity, tenantId, orgScope) => {
    let query = deps.getDb().selectFrom("search_tokens").select(sql`1`.as("one")).where("entity_type", "=", entity);
    query = tenantId == null ? query.where("tenant_id", "is", null) : query.where("tenant_id", "=", tenantId);
    if (orgScope) {
      query = deps.applyOrganizationScope(query, "search_tokens.organization_id", orgScope);
    }
    const row = await query.limit(1).executeTakeFirst();
    return !!row;
  };
  const hasTokens = (entity, tenantId, orgScope) => {
    const key = `${entity}|${tenantId ?? "__null__"}|${orgScopeKey(orgScope)}`;
    const ttlMs = resolvePresenceCacheTtlMs();
    if (ttlMs > 0) {
      const entry = presenceCache.get(key);
      if (entry && entry.expiresAt > Date.now()) return Promise.resolve(entry.value);
    }
    const cached = tokenPresence.get(key);
    if (cached) return cached;
    const probe = (async () => {
      try {
        const value = await probeTokens(entity, tenantId, orgScope);
        if (ttlMs > 0) storePresence(key, value, ttlMs);
        return value;
      } catch (err) {
        deps.logDebug("search:has-tokens-error", {
          entity,
          tenantId,
          organizationScope: orgScope,
          error: err instanceof Error ? err.message : String(err)
        });
        return false;
      }
    })();
    tokenPresence.set(key, probe);
    return probe;
  };
  return {
    staticEnabled: async () => deps.getConfig().enabled && await tableExists("search_tokens"),
    hasTokens,
    anySourceHasTokens: async (sources, tenantId, orgScope) => {
      for (const source of sources) {
        const ok = await hasTokens(source.entity, tenantId, orgScope);
        deps.logDebug("search:source-has-tokens", {
          entity: source.entity,
          recordIdColumn: source.recordIdColumn,
          tenantId,
          organizationScope: orgScope,
          hasTokens: ok
        });
        if (ok) return true;
      }
      return false;
    }
  };
}
export {
  clearSearchTokenPresenceCache,
  createSearchTokenAvailability,
  hasSearchFilter,
  isSearchFilterOp
};
//# sourceMappingURL=availability.js.map
