(function attachPyroHistoryAssets(root) {
  'use strict';

  const ASSET_PREFIX = 'pyro-history-asset:';
  const deepClone = (value) => JSON.parse(JSON.stringify(value));

  function createStore() {
    return { byValue: new Map(), byId: new Map() };
  }

  function compactDocument(document, store, { createId, threshold = 4096 } = {}) {
    const copy = deepClone(document); const nextId = typeof createId === 'function' ? createId : (prefix) => `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
    const internObject = function internObject(object) {
      const intern = (owner, key) => {
        if (typeof owner?.[key] !== 'string' || owner[key].length <= threshold) return;
        let id = store.byValue.get(owner[key]);
        if (!id) { id = nextId('history-asset'); store.byValue.set(owner[key], id); store.byId.set(id, owner[key]); }
        owner[key] = `${ASSET_PREFIX}${id}`;
      };
      intern(object, 'href');
      intern(object.textTexture, 'href');
      intern(object.fillPaint, 'href');
      object.children?.forEach(internObject);
    };
    copy.layers?.forEach((layer) => layer.objects?.forEach(internObject));
    copy.resources?.symbols?.forEach((symbol) => { if (symbol.object) internObject(symbol.object); });
    copy.resources?.objectStyles?.forEach((preset) => { const owner = preset.style?.fillPaint; if (typeof owner?.href === 'string' && owner.href.length > threshold) {
      let id = store.byValue.get(owner.href); if (!id) { id = nextId('history-asset'); store.byValue.set(owner.href, id); store.byId.set(id, owner.href); } owner.href = `${ASSET_PREFIX}${id}`;
    } });
    copy.resources?.fillPresets?.forEach((preset) => { const owner = preset.paint; if (typeof owner?.href === 'string' && owner.href.length > threshold) {
      let id = store.byValue.get(owner.href); if (!id) { id = nextId('history-asset'); store.byValue.set(owner.href, id); store.byId.set(id, owner.href); } owner.href = `${ASSET_PREFIX}${id}`;
    } });
    return copy;
  }

  function hydrateDocument(value, store) {
    const document = typeof value === 'string' ? JSON.parse(value) : deepClone(value);
    const hydrateObject = function hydrateObject(object) {
      const hydrate = (owner, key) => {
        if (typeof owner?.[key] !== 'string' || !owner[key].startsWith(ASSET_PREFIX)) return;
        const href = store.byId.get(owner[key].slice(ASSET_PREFIX.length));
        if (typeof href !== 'string') throw new Error('Ein Bitmap-Zustand im Verlauf ist nicht mehr verfügbar');
        owner[key] = href;
      };
      hydrate(object, 'href');
      hydrate(object.textTexture, 'href');
      hydrate(object.fillPaint, 'href');
      object.children?.forEach(hydrateObject);
    };
    const hydrateHref = (owner) => {
      if (typeof owner?.href !== 'string' || !owner.href.startsWith(ASSET_PREFIX)) return;
      const href = store.byId.get(owner.href.slice(ASSET_PREFIX.length)); if (typeof href !== 'string') throw new Error('Ein Bibliotheks-Bildzustand ist nicht mehr verfügbar'); owner.href = href;
    };
    document.layers?.forEach((layer) => layer.objects?.forEach(hydrateObject));
    document.resources?.symbols?.forEach((symbol) => { if (symbol.object) hydrateObject(symbol.object); });
    document.resources?.objectStyles?.forEach((preset) => hydrateHref(preset.style?.fillPaint));
    document.resources?.fillPresets?.forEach((preset) => hydrateHref(preset.paint));
    return document;
  }

  function storeBytes(store) {
    let bytes = 0; store.byId.forEach((value) => { bytes += value.length * 2; }); return bytes;
  }

  function prune(store, serializedStates = []) {
    const referenced = new Set(); const expression = new RegExp(`${ASSET_PREFIX}([^"\\\\]+)`, 'g');
    serializedStates.filter(Boolean).forEach((value) => {
      expression.lastIndex = 0;
      for (const match of String(value).matchAll(expression)) referenced.add(match[1]);
    });
    store.byId.forEach((value, id) => {
      if (referenced.has(id)) return;
      store.byId.delete(id); store.byValue.delete(value);
    });
    return referenced.size;
  }

  const api = Object.freeze({ ASSET_PREFIX, createStore, compactDocument, hydrateDocument, storeBytes, prune });
  root.PyroHistoryAssets = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : window);
