(function(){
  'use strict';

  if (window.__mnoteLocalFolderEventBus) {
    return;
  }

  var connections = new Map();
  var sidebarRefreshQueues = new Map();
  var lastSource = '';
  var lastReason = '';

  function root() {
    return document.documentElement;
  }

  function setDiagnostics(source, reason) {
    lastSource = source || lastSource || '';
    lastReason = reason || lastReason || '';
    root().setAttribute('data-mnote-local-folder-event-bus', 'ready');
    root().setAttribute('data-mnote-local-folder-event-bus-connections', String(connections.size));
    root().setAttribute('data-mnote-local-folder-event-bus-last-source', lastSource);
    root().setAttribute('data-mnote-local-folder-event-bus-last-reason', lastReason);
  }

  function normalizeRootUri(rootUri) {
    return String(rootUri || '').trim();
  }

  function normalizeWorkspaceId(workspaceId) {
    return String(workspaceId || 'default').trim() || 'default';
  }

  function emit(name, detail) {
    window.dispatchEvent(new CustomEvent(name, { detail: detail || {} }));
  }

  function parseEventPayload(event) {
    try {
      return JSON.parse((event && event.data) || '{}') || {};
    } catch (_) {
      return {};
    }
  }

  function revisionOf(payload, event) {
    return payload.revision || payload.cursor || (event && event.lastEventId) || null;
  }

  function arrayOf(value) {
    if (!value) return [];
    if (Array.isArray(value)) return value.filter(Boolean).map(String);
    return [String(value)].filter(Boolean);
  }

  function pathArrayOf(value) {
    if (!value) return [];
    var list = Array.isArray(value) ? value : [value];
    return list.map(function(item) {
      if (typeof item === 'string') return item.trim();
      if (!item || typeof item !== 'object') return '';
      return String(item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '').trim();
    }).filter(Boolean);
  }

  function collectChangedPaths(payload) {
    var paths = []
      .concat(pathArrayOf(payload.changedPaths))
      .concat(pathArrayOf(payload.changed_paths))
      .concat(pathArrayOf(payload.paths));
    var events = Array.isArray(payload.events) ? payload.events : [];
    events.forEach(function(item) {
      if (!item || typeof item !== 'object') return;
      paths = paths
        .concat(arrayOf(item.path))
        .concat(arrayOf(item.relativePath))
        .concat(arrayOf(item.relative_path))
        .concat(arrayOf(item.sourcePath))
        .concat(arrayOf(item.source_path));
    });
    return Array.from(new Set(paths));
  }

  function collectChangedPathItems(payload, reason) {
    var raw = []
      .concat(Array.isArray(payload.changedPaths) ? payload.changedPaths : [])
      .concat(Array.isArray(payload.changed_paths) ? payload.changed_paths : [])
      .concat(Array.isArray(payload.paths) ? payload.paths : [])
      .concat(Array.isArray(payload.events) ? payload.events : []);
    var seen = new Set();
    var items = [];
    raw.forEach(function(item) {
      var relativePath = typeof item === 'string'
        ? item.trim()
        : String(item && (item.relativePath || item.relative_path || item.path || item.sourcePath || item.source_path || '') || '').trim();
      if (!relativePath || seen.has(relativePath)) return;
      seen.add(relativePath);
      items.push({
        relativePath: relativePath,
        reason: reason || 'event-bus',
        kind: typeof item === 'object' && item ? String(item.kind || '') : '',
        eventKind: typeof item === 'object' && item ? String(item.eventKind || item.event_kind || item.changeType || item.change_type || '') : ''
      });
    });
    if (items.length) return items;
    return pathItems(collectChangedPaths(payload), reason);
  }

  function collectAffectedParents(payload) {
    var parents = []
      .concat(pathArrayOf(payload.affectedParents))
      .concat(pathArrayOf(payload.affected_parents))
      .concat(pathArrayOf(payload.parentRelativePaths))
      .concat(pathArrayOf(payload.parent_relative_paths));
    return Array.from(new Set(parents));
  }

  function pathItems(paths, reason) {
    return (paths || []).map(function(relativePath) {
      return {
        relativePath: String(relativePath || '').trim(),
        reason: reason || 'event-bus'
      };
    }).filter(function(item) { return item.relativePath || item.relativePath === ''; });
  }

  function queueSidebarRefresh(detail) {
    var key = String(detail.rootUri || '').trim();
    if (!key) return;
    var queue = sidebarRefreshQueues.get(key);
    if (!queue) {
      queue = {
        rootUri: detail.rootUri,
        workspaceId: detail.workspaceId,
        revisions: new Set(),
        reasons: new Set(),
        changedPaths: new Map(),
        affectedParents: new Set(),
        resyncRequired: false,
        timer: 0
      };
      sidebarRefreshQueues.set(key, queue);
    }
    if (detail.revision) queue.revisions.add(String(detail.revision));
    if (detail.reason) queue.reasons.add(String(detail.reason));
    (detail.changedPaths || []).forEach(function(item) {
      var path = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || '');
      if (!path && path !== '') return;
      var existing = queue.changedPaths.get(path) || { relativePath: path };
      queue.changedPaths.set(path, {
        relativePath: path,
        reason: String((item && item.reason) || existing.reason || detail.reason || 'event-bus-orchestrated'),
        kind: String((item && item.kind) || existing.kind || ''),
        eventKind: String((item && item.eventKind) || (item && item.event_kind) || existing.eventKind || '')
      });
    });
    (detail.affectedParents || []).forEach(function(item) {
      var parent = typeof item === 'string' ? item : String(item && (item.relativePath || item.relative_path || '') || '');
      if (parent || parent === '') queue.affectedParents.add(parent);
    });
    queue.resyncRequired = queue.resyncRequired || detail.resyncRequired === true;
    if (!queue.timer) {
      queue.timer = window.setTimeout(function() {
        flushSidebarRefresh(key);
      }, 0);
    }
    root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-pending', String(queue.affectedParents.size));
  }

  function flushSidebarRefresh(key) {
    var queue = sidebarRefreshQueues.get(key);
    if (!queue) return;
    sidebarRefreshQueues.delete(key);
    var reasons = Array.from(queue.reasons);
    var detail = {
      schema: 'mnote.local_folder.event_bus.sidebar_refresh.v1',
      source: 'event_bus_orchestrator',
      reason: reasons.join(',') || 'watch_batch',
      rootUri: queue.rootUri,
      workspaceId: queue.workspaceId,
      revision: Array.from(queue.revisions).pop() || null,
      reasons: reasons,
      changedPaths: Array.from(queue.changedPaths.values()),
      affectedParents: pathItems(Array.from(queue.affectedParents), 'event-bus-orchestrated'),
      resyncRequired: queue.resyncRequired,
      viaEventBus: true
    };
    root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-applied', detail.reason);
    root().setAttribute('data-mnote-local-folder-event-bus-sidebar-refresh-parents', String(detail.affectedParents.length));
    emit('mnote:local-folder:sidebar-refresh-requested', detail);
  }

  function dispatchWatchBatch(entry, event, payload, meta) {
    var revision = revisionOf(payload, event);
    var changedPaths = collectChangedPaths(payload);
    var affectedParents = collectAffectedParents(payload);
    var source = String((meta && meta.source) || payload.source || 'watcher_sse').trim() || 'watcher_sse';
    var reason = String((meta && meta.reason) || payload.reason || 'watch_batch').trim() || 'watch_batch';
    var resyncRequired = payload.fallbackResync === true
      || payload.requiresResync === true
      || payload.resyncRequired === true
      || payload.resync_required === true;
    var detail = {
      schema: 'mnote.local_folder.event_bus.watch_batch.v1',
      source: source,
      reason: reason,
      rootUri: entry.rootUri,
      workspaceId: entry.workspaceId,
      revision: revision,
      payload: payload,
      bootstrap: entry.bootstrap || null,
      changedPaths: collectChangedPathItems(payload, reason),
      affectedParents: pathItems(affectedParents, reason),
      resyncRequired: resyncRequired,
      viaEventBus: true
    };

    setDiagnostics(source, reason);
    root().setAttribute('data-mnote-tree-live-revision', String(revision || ''));
    emit('mnote:local-folder:watch-batch', detail);
    emit('tree:local-folder-watch-batch', detail);

    if (affectedParents.length > 0) {
      emit('mnote:local-folder:filetree-parent-changed', detail);
    }
    if (changedPaths.length > 0) {
      emit('mnote:local-folder:document-changed', detail);
      emit('mnote:local-folder:resource-changed', detail);
      emit('mnote:local-folder:knowledge-rag-source-updated', detail);
    }
    queueSidebarRefresh(detail);
    if (resyncRequired) {
      emit('mnote:local-folder:resync-required', detail);
    }
  }

  function closeEntry(entry) {
    if (!entry || !entry.source || typeof entry.source.close !== 'function') return;
    entry.source.close();
  }

  function startLocalFolderWatcher(options) {
    var rootUri = normalizeRootUri(options && options.rootUri);
    if (!rootUri || typeof window.EventSource !== 'function') return null;
    var workspaceId = normalizeWorkspaceId(options && options.workspaceId);
    var key = rootUri;
    if (connections.has(key)) {
      var existing = connections.get(key);
      if ((!existing.workspaceId || existing.workspaceId === 'default') && workspaceId && workspaceId !== 'default') {
        existing.workspaceId = workspaceId;
        if (existing.handle) existing.handle.workspaceId = workspaceId;
      }
      setDiagnostics(existing.lastSource || 'watcher_sse', 'reuse_connection');
      return existing.handle;
    }

    var url = new URL('/api/local-folder/events', window.location.origin);
    url.searchParams.set('rootUri', rootUri);
    url.searchParams.set('treeLive', 'true');

    var eventSource = new EventSource(url.toString());
    var entry = {
      key: key,
      rootUri: rootUri,
      workspaceId: workspaceId,
      bootstrap: (options && options.bootstrap) || null,
      source: eventSource,
      lastSource: 'watcher_sse',
      close: function() {
        connections.delete(key);
        closeEntry(entry);
        setDiagnostics('watcher_sse', 'closed');
      }
    };
    entry.handle = {
      key: key,
      rootUri: rootUri,
      workspaceId: workspaceId,
      source: eventSource,
      close: entry.close
    };
    connections.set(key, entry);
    setDiagnostics('watcher_sse', 'connect');
    emit('mnote:local-folder:event-bus-ready', {
      schema: 'mnote.local_folder.event_bus.ready.v1',
      source: 'watcher_sse',
      reason: 'connect',
      rootUri: rootUri,
      workspaceId: workspaceId,
      connections: connections.size
    });

    eventSource.addEventListener('open', function() {
      setDiagnostics('watcher_sse', 'open');
    });
    eventSource.addEventListener('watch_batch', function(event) {
      dispatchWatchBatch(entry, event, parseEventPayload(event), {
        source: 'watcher_sse',
        reason: 'watch_batch'
      });
    });
    eventSource.addEventListener('tree_error', function(event) {
      var payload = parseEventPayload(event);
      setDiagnostics('watcher_sse', 'tree_error');
      emit('tree:error', { payload: payload, bootstrap: entry.bootstrap || null, viaEventBus: true });
    });
    eventSource.onerror = function() {
      setDiagnostics('watcher_sse', 'error');
    };

    return entry.handle;
  }

  function emitSyntheticWatchBatch(detail) {
    var payload = detail && detail.payload ? detail.payload : (detail || {});
    var rootUri = normalizeRootUri(detail && detail.rootUri);
    var workspaceId = normalizeWorkspaceId(detail && detail.workspaceId);
    var entry = {
      rootUri: rootUri,
      workspaceId: workspaceId,
      bootstrap: (detail && detail.bootstrap) || null
    };
    dispatchWatchBatch(entry, { lastEventId: detail && detail.revision }, payload, {
      source: (detail && detail.source) || 'synthetic',
      reason: (detail && detail.reason) || payload.source || 'synthetic_watch_batch'
    });
  }

  function closeAll() {
    Array.from(connections.values()).forEach(function(entry) {
      closeEntry(entry);
    });
    connections.clear();
    setDiagnostics('watcher_sse', 'closed');
  }

  window.__mnoteLocalFolderEventBus = {
    startLocalFolderWatcher: startLocalFolderWatcher,
    emitSyntheticWatchBatch: emitSyntheticWatchBatch,
    closeAll: closeAll,
    flushSidebarRefresh: function(rootUri) {
      flushSidebarRefresh(normalizeRootUri(rootUri));
    },
    connectionCount: function() { return connections.size; },
    diagnostics: function() {
      return {
        connections: connections.size,
        lastSource: lastSource,
        lastReason: lastReason
      };
    }
  };

  setDiagnostics('', 'ready');
})();
