// MNote document conflict panel 运行时外置模块。
// 第一刀：纯 DOM 清理 helper clearSessionConflictSurface。
// 本文件以 type="module" 加载，执行时机可能晚于 inline script。
// web_shell.rs 的 inline clearSessionConflictSurface 会优先委托到
// window.__mnoteDocumentConflictPanelRuntime；
// 模块尚未加载或加载失败时，inline script 保留原地实现兜底。

/**
 * 清除指定 session 所有视图中冲突面板 DOM。
 * 只做 DOM 清理，不处理冲突 merge/accept/keep 业务逻辑。
 * @param {Object} session - document session 对象
 * @param {Object} deps - 依赖注入，从 inline 上下文传入
 * @param {Function} deps.sessionViews - session.views 迭代器
 */
function clearSessionConflictSurface(session, deps) {
  var views = deps && typeof deps.sessionViews === 'function'
    ? deps.sessionViews(session)
    : [];
  views.forEach(function(view) {
    var host = conflictPanelHost(view && view.runtimeDescriptor && view.runtimeDescriptor.root);
    if (!(host instanceof HTMLElement)) return;
    host.querySelectorAll('[data-testid="mnote-editor-conflict-panel"]').forEach(function(node) { node.remove(); });
  });
}

function conflictPanelHost(root) {
  if (!(root instanceof Element)) return null;
  return root.closest('.document-pane') || root;
}

function mountSessionConflictPanel(root, panel) {
  var host = conflictPanelHost(root);
  if (!(host instanceof HTMLElement) || !(panel instanceof HTMLElement)) return false;
  var header = host.querySelector('.document-shell-header');
  if (header && header.parentNode) {
    header.parentNode.insertBefore(panel, header.nextSibling);
  } else {
    host.prepend(panel);
  }
  return true;
}

function createSessionConflictPanel(session, message, deps) {
  deps = deps || {};
  var externalConflictMessage = String(deps.externalConflictMessage || '').trim() || '文件已在外部修改';
  var panel = document.createElement('section');
  panel.className = 'mnote-editor-conflict-panel';
  panel.setAttribute('data-testid', 'mnote-editor-conflict-panel');
  panel.setAttribute('role', 'status');
  panel.setAttribute('aria-live', 'polite');

  var heading = document.createElement('h2');
  heading.textContent = '文件冲突';
  var text = document.createElement('p');
  text.textContent = message || externalConflictMessage;
  var meta = document.createElement('div');
  meta.className = 'mnote-conflict-meta';
  var fileLabel = session && session.sessionKind === 'resource'
    ? String(session.rootUri || '') + '/' + String(session.resourcePath || session.documentId || '')
    : String(session && (session.rootUri || session.documentId) || '');
  var sourceLabel = typeof deps.conflictSourceLabel === 'function'
    ? deps.conflictSourceLabel(session)
    : '';
  meta.textContent = '文件：' + fileLabel + ' · 来源：' + (sourceLabel || '本地文件变更');

  var actions = document.createElement('div');
  actions.className = 'mnote-conflict-actions';
  var acceptDisk = document.createElement('button');
  acceptDisk.type = 'button';
  acceptDisk.textContent = '接受磁盘版本';
  acceptDisk.setAttribute('data-testid', 'mnote-conflict-accept-disk');
  var keepCurrent = document.createElement('button');
  keepCurrent.type = 'button';
  keepCurrent.textContent = '保留当前编辑器版本';
  keepCurrent.setAttribute('data-testid', 'mnote-conflict-keep-current');
  var openDiff = document.createElement('button');
  openDiff.type = 'button';
  openDiff.textContent = '打开 diff';
  openDiff.setAttribute('data-testid', 'mnote-conflict-open-diff');
  actions.append(acceptDisk, keepCurrent, openDiff);

  var diffPanel = document.createElement('div');
  diffPanel.className = 'mnote-conflict-diff-panel';
  diffPanel.setAttribute('data-testid', 'mnote-conflict-diff-panel');
  diffPanel.hidden = true;
  panel.append(heading, text, meta, actions, diffPanel);

  acceptDisk.addEventListener('click', function() {
    if (typeof deps.onAcceptDisk === 'function') deps.onAcceptDisk(session, panel);
  });
  keepCurrent.addEventListener('click', function() {
    if (typeof deps.onKeepCurrent === 'function') deps.onKeepCurrent(session, panel);
  });
  openDiff.addEventListener('click', function() {
    if (typeof deps.onOpenDiff === 'function') deps.onOpenDiff(session, panel);
  });

  return panel;
}

/**
 * 在已有 diffPanel 中填充 current/disk/merge DOM 并绑定回调。
 * 不处理 fetch、persist、status 等业务逻辑。
 * @param {HTMLElement} panel - 已清空的 diffPanel 元素
 * @param {Object} context
 * @param {string} context.currentText - 当前编辑器的文本（含占位文案）
 * @param {string} context.diskText - 磁盘版本的文本（含占位文案）
 * @param {string} [context.currentMergeText] - 写入合并区域的当前编辑器原始文本
 * @param {string} [context.diskMergeText] - 写入合并区域的磁盘版本原始文本
 * @param {string} [context.mergeDefaultText] - 合并区域默认文本
 * @param {Object} deps
 * @param {Function} [deps.onUseCurrent] - 可选：返回当前版本写入合并区域的文本
 * @param {Function} [deps.onUseDisk] - 可选：返回磁盘版本写入合并区域的文本
 * @param {Function} deps.onSaveMerge - 保存合并结果回调，接收合并文本字符串
 */
function populateSessionConflictDiffPanel(panel, context, deps) {
  context = context || {};
  deps = deps || {};
  var currentText = context.currentText || '';
  var diskText = context.diskText || '';
  var currentMergeText = Object.prototype.hasOwnProperty.call(context, 'currentMergeText')
    ? String(context.currentMergeText || '')
    : currentText;
  var diskMergeText = Object.prototype.hasOwnProperty.call(context, 'diskMergeText')
    ? String(context.diskMergeText || '')
    : diskText;
  var mergeDefaultText = context.mergeDefaultText || currentText || diskText || '';

  var current = document.createElement('pre');
  current.setAttribute('data-testid', 'mnote-conflict-current-text');
  current.textContent = currentText;
  var disk = document.createElement('pre');
  disk.setAttribute('data-testid', 'mnote-conflict-disk-text');
  disk.textContent = diskText;
  var currentTitle = document.createElement('h3');
  currentTitle.textContent = '当前编辑器版本';
  var diskTitle = document.createElement('h3');
  diskTitle.textContent = '磁盘版本';
  var currentBox = document.createElement('section');
  currentBox.append(currentTitle, current);
  var diskBox = document.createElement('section');
  diskBox.append(diskTitle, disk);
  var mergeTitle = document.createElement('h3');
  mergeTitle.textContent = '合并结果';
  var mergeText = document.createElement('textarea');
  mergeText.setAttribute('data-testid', 'mnote-conflict-merge-text');
  mergeText.value = mergeDefaultText;
  var mergeActions = document.createElement('div');
  mergeActions.className = 'mnote-conflict-actions';
  var useCurrent = document.createElement('button');
  useCurrent.type = 'button';
  useCurrent.textContent = '使用当前版本';
  useCurrent.setAttribute('data-testid', 'mnote-conflict-merge-use-current');
  var useDisk = document.createElement('button');
  useDisk.type = 'button';
  useDisk.textContent = '使用磁盘版本';
  useDisk.setAttribute('data-testid', 'mnote-conflict-merge-use-disk');
  var saveMerge = document.createElement('button');
  saveMerge.type = 'button';
  saveMerge.textContent = '写回合并结果';
  saveMerge.setAttribute('data-testid', 'mnote-conflict-merge-save');
  mergeActions.append(useCurrent, useDisk, saveMerge);
  var mergeBox = document.createElement('section');
  mergeBox.className = 'mnote-conflict-merge-box';
  mergeBox.append(mergeTitle, mergeText, mergeActions);
  panel.append(currentBox, diskBox, mergeBox);

  useCurrent.addEventListener('click', function() {
    var nextText = typeof deps.onUseCurrent === 'function'
      ? deps.onUseCurrent(context)
      : currentMergeText;
    mergeText.value = typeof nextText === 'string' ? nextText : currentMergeText;
  });
  useDisk.addEventListener('click', function() {
    var nextText = typeof deps.onUseDisk === 'function'
      ? deps.onUseDisk(context)
      : diskMergeText;
    mergeText.value = typeof nextText === 'string' ? nextText : diskMergeText;
  });
  saveMerge.addEventListener('click', function() {
    if (typeof deps.onSaveMerge === 'function') {
      deps.onSaveMerge(mergeText.value);
    }
  });
}

function runSessionConflictAction(action, deps) {
  deps = deps || {};
  if (typeof action !== 'function') return;
  try {
    var result = action();
    if (result && typeof result.then === 'function') {
      result.catch(function(error) {
        if (typeof deps.onError === 'function') deps.onError(error);
      });
    }
  } catch (error) {
    if (typeof deps.onError === 'function') deps.onError(error);
  }
}

/**
 * 接受磁盘版本：用磁盘快照替换当前 session 内容。
 * 依赖通过 deps 注入，确保 runtime 不依赖 inline 作用域闭包。
 * @param {Object} session
 * @param {Object} deps
 * @param {Function} deps.setSessionStatus
 * @param {Function} deps.fetchLatestResourceSnapshot
 * @param {Function} deps.applyResourceSnapshotToSession
 * @param {Function} deps.fetchLatestSessionAggregate
 * @param {Function} deps.applyAggregateSnapshotToSession
 */
function acceptDiskVersion(session, deps) {
  deps = deps || {};
  var setSessionStatus = deps.setSessionStatus || function() {};
  var fetchLatestResourceSnapshot = deps.fetchLatestResourceSnapshot || function() { return Promise.resolve(null); };
  var applyResourceSnapshotToSession = deps.applyResourceSnapshotToSession || function() {};
  var fetchLatestSessionAggregate = deps.fetchLatestSessionAggregate || function() { return Promise.resolve(null); };
  var applyAggregateSnapshotToSession = deps.applyAggregateSnapshotToSession || function() {};

  setSessionStatus(session, 'conflict-resolving', '正在接受磁盘版本...');
  if (session.sessionKind === 'resource') {
    return fetchLatestResourceSnapshot(session).then(function(latestResource) {
      applyResourceSnapshotToSession(session, latestResource, 'mnote-web-resource-conflict-accept-disk');
    });
  }
  return fetchLatestSessionAggregate(session).then(function(latest) {
    applyAggregateSnapshotToSession(session, latest, 'mnote-web-conflict-accept-disk');
  });
}

/**
 * 保留当前编辑器版本：读取 editor 实时文本，更新 conflictDetectionKey，
 * 清空冲突标记，持久化后关闭冲突面板。
 * 依赖通过 deps 注入。
 * @param {Object} session
 * @param {Object} deps
 * @param {Function} deps.setSessionStatus
 * @param {Function} deps.fetchLatestResourceSnapshot - 返回 Promise<Object>
 * @param {Function} deps.fetchLatestSessionAggregate - 返回 Promise<Object>
 * @param {Function} deps.sessionViews - session → views[]
 * @param {Function} deps.currentEditorText - view → string
 * @param {Function} deps.normalizePlainText - string → string
 * @param {Function} deps.hydrateMindmapAttrsFromDom - (doc, root) → doc
 * @param {Function} deps.textToTiptapDocument - string → doc
 * @param {Function} deps.conflictDetectionKeyFromBody - body → string
 * @param {Function} deps.clearSessionConflictSurface - session → void
 * @param {Function} deps.persistSession - session → Promise
 */
function keepCurrentEditorVersion(session, deps) {
  deps = deps || {};
  var setSessionStatus = deps.setSessionStatus || function() {};
  var fetchLatestResourceSnapshot = deps.fetchLatestResourceSnapshot || function() { return Promise.resolve(null); };
  var fetchLatestSessionAggregate = deps.fetchLatestSessionAggregate || function() { return Promise.resolve(null); };
  var sessionViews = deps.sessionViews || function() { return []; };
  var currentEditorText = deps.currentEditorText || function() { return ''; };
  var normalizePlainText = deps.normalizePlainText || function(s) { return s; };
  var hydrateMindmapAttrsFromDom = deps.hydrateMindmapAttrsFromDom || function(doc) { return doc; };
  var textToTiptapDocument = deps.textToTiptapDocument || function(s) { return {type: 'doc', content: [{type: 'paragraph', content: [{type: 'text', text: s}]}]}; };
  var conflictDetectionKeyFromBody = deps.conflictDetectionKeyFromBody || function() { return ''; };
  var clearSessionConflictSurface = deps.clearSessionConflictSurface || function() {};
  var persistSession = deps.persistSession || function() { return Promise.resolve(); };

  setSessionStatus(session, 'conflict-resolving', '正在保留当前编辑器版本...');
  var latestPromise = session.sessionKind === 'resource'
    ? fetchLatestResourceSnapshot(session)
    : fetchLatestSessionAggregate(session);
  return latestPromise.then(function(latest) {
    var hydrateView = sessionViews(session).find(function(item) { return item.mountId != null; }) || sessionViews(session)[0];
    if (hydrateView) {
      var liveText = normalizePlainText(currentEditorText(hydrateView));
      if (liveText) {
        session.currentTiptapDocument = hydrateMindmapAttrsFromDom(
          textToTiptapDocument(liveText),
          hydrateView.runtimeDescriptor && hydrateView.runtimeDescriptor.root,
        );
      }
      session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
    }
    var nextKey = session.sessionKind === 'resource'
      ? String((latest && (latest.fileVersion || latest.conflictDetectionKey) || '')).trim()
      : conflictDetectionKeyFromBody((latest && latest.body) || {});
    if (nextKey) {
      session.conflictDetectionKey = nextKey;
      session.lastExternalConflictDetectionKey = nextKey;
    }
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.saving = false;
    session.dirty = true;
    clearSessionConflictSurface(session);
    return persistSession(session);
  });
}

/**
 * 写回合并结果：将 merge textarea 中的文本写入 session，
 * 更新 conflictDetectionKey，清除冲突标记，持久化后移除面板。
 * @param {Object} session
 * @param {HTMLElement|null} panel - 待移除的面板 DOM
 * @param {string} mergedText
 * @param {Object} deps
 * @param {Function} deps.setSessionStatus
 * @param {Function} deps.fetchLatestResourceSnapshot - Promise<Object>
 * @param {Function} deps.fetchLatestSessionAggregate - Promise<Object>
 * @param {Function} deps.conflictDetectionKeyFromBody - body → string
 * @param {Function} deps.textToTiptapDocument - string → doc
 * @param {Function} deps.persistSession - session → Promise
 */
function writeMergedConflictResult(session, panel, mergedText, deps) {
  deps = deps || {};
  var setSessionStatus = deps.setSessionStatus || function() {};
  var fetchLatestResourceSnapshot = deps.fetchLatestResourceSnapshot || function() { return Promise.resolve(null); };
  var fetchLatestSessionAggregate = deps.fetchLatestSessionAggregate || function() { return Promise.resolve(null); };
  var conflictDetectionKeyFromBody = deps.conflictDetectionKeyFromBody || function() { return ''; };
  var textToTiptapDocument = deps.textToTiptapDocument || function(s) { return {type: 'doc', content: [{type: 'paragraph', content: [{type: 'text', text: s}]}]}; };
  var persistSession = deps.persistSession || function() { return Promise.resolve(); };

  setSessionStatus(session, 'conflict-resolving', '正在写回合并结果...');
  var latestPromise = session.sessionKind === 'resource'
    ? fetchLatestResourceSnapshot(session)
    : fetchLatestSessionAggregate(session);
  return latestPromise.then(function(latest) {
    var nextKey = session.sessionKind === 'resource'
      ? String((latest && (latest.fileVersion || latest.conflictDetectionKey) || '')).trim()
      : conflictDetectionKeyFromBody((latest && latest.body) || {});
    if (nextKey) {
      session.conflictDetectionKey = nextKey;
      session.lastExternalConflictDetectionKey = nextKey;
    }
    session.currentTiptapDocument = textToTiptapDocument(String(mergedText || ''));
    session.currentSerialized = JSON.stringify(session.currentTiptapDocument);
    session.hasExternalConflict = false;
    session.externalChangePending = false;
    session.saving = false;
    session.dirty = true;
    if (panel && typeof panel.remove === 'function') panel.remove();
    return persistSession(session);
  });
}

window.__mnoteDocumentConflictPanelRuntime = {
  clearSessionConflictSurface: clearSessionConflictSurface,
  createSessionConflictPanel: createSessionConflictPanel,
  mountSessionConflictPanel: mountSessionConflictPanel,
  populateSessionConflictDiffPanel: populateSessionConflictDiffPanel,
  runSessionConflictAction: runSessionConflictAction,
  acceptDiskVersion: acceptDiskVersion,
  keepCurrentEditorVersion: keepCurrentEditorVersion,
  writeMergedConflictResult: writeMergedConflictResult
};
