// MNote 本地上传运行时外置模块。
// 当前先承接 SIDEBAR_TREE_JS 中的纯辅助函数。
// SIDEBAR_TREE_JS 会优先委托到 window.__mnoteLocalUploadRuntime；
// 模块未加载时，inline fallback 保持旧行为。
import {
  editorDocumentFromTiptapDocument,
  legacyBlocksFromEditorDocument,
} from './document-tiptap-conversion-runtime.js';

function uploadedAssetTitle(asset) {
  return String(asset && (asset.file_name || asset.title || asset.name) || '未命名附件').trim() || '未命名附件';
}

function uploadedAssetUrl(asset) {
  return String(asset && (asset.sourcePath || asset.file_url || asset.signedUrl || asset.signed_url || asset.thumbnail_url) || '').trim();
}

function dispatchUploadedEditorChange(editorRoot, editor, deps) {
  deps = deps || {};
  if (!(editorRoot instanceof HTMLElement) || !editor || typeof editor.getJSON !== 'function') return;
  var runtimeRoot = editorRoot.closest('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
  if (!(runtimeRoot instanceof HTMLElement) || typeof CustomEvent !== 'function') return;
  try {
    editorRoot.dispatchEvent(new Event('input', { bubbles: true }));
  } catch (_) {
    // ignore
  }
  var currentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId() : '';
  var payload = {
    protocol: 'mnote.leptos_tiptap.bridge.v1',
    runtime: '8123-leptos-tiptap-runtime',
    version: '1.1.0',
    source: 'mnote:leptos-tiptap-spike',
    event: 'mnote:leptos-tiptap-spike:change',
    payload: {
      documentId: String(currentDocumentId || '').trim() || null,
      workspaceId: null,
      title: '',
      content: editor.getJSON(),
      meta: {
        dirtyCount: Date.now(),
        editorFocused: document.activeElement === editorRoot || editorRoot.contains(document.activeElement),
        slashOpen: false,
        toolbarOpen: false,
        selectedBlockIndex: null,
        revision: null,
        conflictDetectionKey: null,
        readOnly: false
      }
    }
  };
  runtimeRoot.dispatchEvent(new CustomEvent('mnote:leptos-tiptap-spike:change', {
    detail: payload,
    bubbles: true
  }));
}

function currentConflictDetectionKey() {
  var script = document.getElementById('__MNOTE_PAGE_AGGREGATE__');
  if (!script) return '';
  try {
    var body = JSON.parse(script.textContent || 'null');
    return String(body && (body.conflictDetectionKey || body.conflict_detection_key || body.fileVersion || body.file_version) || '').trim();
  } catch (_) {
    return '';
  }
}

function persistLocalFolderSelfChangeSuppression(documentId, expiresAt) {
  var doc = String(documentId || '').trim();
  if (!doc) return;
  window.__mnoteLocalFolderSelfChangeSuppressions = window.__mnoteLocalFolderSelfChangeSuppressions || new Map();
  window.__mnoteLocalFolderSelfChangeSuppressions.set(doc, expiresAt);
  try {
    var key = 'mnote.localFolder.selfChangeSuppressions.v1';
    var existing = JSON.parse(window.sessionStorage.getItem(key) || '{}');
    existing[doc] = expiresAt;
    window.sessionStorage.setItem(key, JSON.stringify(existing));
  } catch (_) {}
}

async function persistUploadedEditorChange(editor, asset, deps) {
  deps = deps || {};
  var isLocalAsset = typeof deps.isLocalUploadedAsset === 'function' ? deps.isLocalUploadedAsset(asset) : isLocalUploadedAsset(asset);
  if (!isLocalAsset || !editor || typeof editor.getJSON !== 'function') return;
  var documentId = String(
    asset && (asset.document_id || asset.documentId)
    || (typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId() : '')
    || ''
  ).trim();
  var rootUri = String(
    asset && (asset.root_uri || asset.rootUri)
    || (typeof deps.currentRootUri === 'function' ? deps.currentRootUri() : currentRootUri())
    || ''
  ).trim();
  if (!documentId || !rootUri) return;
  var tiptapDocument = editor.getJSON();
  var editorDocument = editorDocumentFromTiptapDocument({ documentId: documentId }, tiptapDocument);
  var content = legacyBlocksFromEditorDocument(editorDocument);
  var savePayload = {
    documentId: documentId,
    sourceKind: 'local_folder',
    rootUri: rootUri,
    expectedFileVersion: currentConflictDetectionKey(),
    contentFormat: 'editorBlocks',
    editorSource: 'local-upload-runtime',
    editorDocument: editorDocument,
    content: content,
    tiptapDocument: tiptapDocument,
    blockCount: Array.isArray(editorDocument.blocks) ? editorDocument.blocks.length : 0
  };
  persistLocalFolderSelfChangeSuppression(documentId, Date.now() + 5000);
  var response = await fetch('/api/documents/save', {
    method: 'POST',
    credentials: 'include',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(savePayload)
  });
  var result = await response.json().catch(function() { return null; });
  if (!response.ok || !result || result.ok !== true) {
    var message = result && (result.message || (result.error && result.error.message)) || ('save_failed_' + response.status);
    document.documentElement.setAttribute('data-mnote-last-upload-save-error', message);
    throw new Error(message);
  }
  document.documentElement.setAttribute('data-mnote-last-upload-saved', 'true');
  document.documentElement.removeAttribute('data-mnote-last-upload-save-error');
}

function currentRootUri() {
  var params = new URLSearchParams(window.location.search);
  var fromUrl = (params.get('rootUri') || '').trim();
  if (fromUrl) return fromUrl;
  return document.body instanceof HTMLElement
    ? (document.body.getAttribute('data-mnote-root-uri') || '').trim()
    : '';
}

function editorUploadRootFromElementWithDeps(target, deps) {
  if (deps && typeof deps.editorUploadRootFromElement === 'function') {
    return deps.editorUploadRootFromElement(target);
  }
  if (!(target instanceof Element)) return null;
  var selector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
  var root = target.closest(selector);
  return root instanceof HTMLElement ? root : null;
}

function resolveEditorUploadContext(detail, deps) {
  var root = null;
  var selector = detail && detail.editorRootSelector ? String(detail.editorRootSelector) : '';
  if (selector) {
    try {
      var selected = document.querySelector(selector);
      if (selected instanceof HTMLElement) root = selected;
    } catch (_) {}
  }
  if (!root && window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) {
    root = window.__mnoteIntendedSlashRoot;
  }
  if (!root && window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) {
    root = window.__mnoteLastEditorUploadRoot;
  }
  if (!root && document.activeElement instanceof Element) {
    root = editorUploadRootFromElementWithDeps(document.activeElement, deps);
  }
  if (!root) {
    var rootSelector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
    var focused = document.querySelector(rootSelector + ' .ProseMirror:focus-within');
    root = editorUploadRootFromElementWithDeps(focused, deps);
  }
  if (!root) {
    root = document.querySelector('[data-editor-host-kind="leptos_tiptap_island"][data-pane-role="primary"]');
  }
  var pane = root instanceof Element ? root.closest('.document-pane[data-pane-role]') : null;
  var shell = root instanceof Element ? root.closest('.document-shell[data-document-id]') : null;
  var currentDocumentId = deps && typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
  var resolveWorkspaceId = deps && typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; };
  return {
    root: root instanceof HTMLElement ? root : null,
    documentId: String(
      detail && detail.documentId
      || (root instanceof HTMLElement && root.getAttribute('data-document-id'))
      || (pane instanceof HTMLElement && pane.getAttribute('data-pane-document-id'))
      || (shell instanceof HTMLElement && shell.getAttribute('data-document-id'))
      || currentDocumentId()
      || ''
    ).trim(),
    workspaceId: String(
      detail && detail.workspaceId
      || (root instanceof HTMLElement && root.getAttribute('data-workspace-id'))
      || (pane instanceof HTMLElement && pane.getAttribute('data-pane-workspace-id'))
      || (shell instanceof HTMLElement && shell.getAttribute('data-workspace-id'))
      || resolveWorkspaceId(document.body)
      || ''
    ).trim()
  };
}

function editorRootFromUploadOptions(options, deps) {
  if (options && options.editorRoot instanceof HTMLElement) return options.editorRoot;
  if (window.__mnoteIntendedSlashRoot instanceof HTMLElement && window.__mnoteIntendedSlashRoot.isConnected) {
    return window.__mnoteIntendedSlashRoot;
  }
  if (window.__mnoteLastEditorUploadRoot instanceof HTMLElement && window.__mnoteLastEditorUploadRoot.isConnected) {
    return window.__mnoteLastEditorUploadRoot;
  }
  var rootSelector = String(deps && deps.rootSelector || '[data-editor-host-kind="leptos_tiptap_island"], [data-editor-host-kind="leptos_tiptap_resource"]');
  var focused = document.querySelector(rootSelector + ' .ProseMirror:focus-within');
  return editorUploadRootFromElementWithDeps(focused, deps);
}

function openEditorUploadFilePicker(detail, deps) {
  var uploadContext = resolveEditorUploadContext(detail || {}, deps || {});
  var input = document.createElement('input');
  input.type = 'file';
  input.multiple = detail && detail.multiple !== false;
  if (detail && detail.accept) input.accept = String(detail.accept);
  input.style.position = 'fixed';
  input.style.left = '-9999px';
  input.style.top = '-9999px';
  document.body.appendChild(input);
  input.addEventListener('change', function() {
    var files = Array.from(input.files || []);
    input.remove();
    var resolveWorkspaceId = deps && typeof deps.resolveWorkspaceId === 'function' ? deps.resolveWorkspaceId : function() { return ''; };
    var currentDocumentId = deps && typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
    var uploadFilesWithResolvedTarget = deps && typeof deps.uploadFilesWithResolvedTarget === 'function' ? deps.uploadFilesWithResolvedTarget : null;
    if (!uploadFilesWithResolvedTarget) return;
    void uploadFilesWithResolvedTarget(files, {
      workspaceId: uploadContext.workspaceId || resolveWorkspaceId(document.body),
      documentId: uploadContext.documentId || currentDocumentId(),
      targetRowId: null,
      uploadIntent: 'editor.markdown.attach'
    }, {
      insertIntoEditor: detail && detail.insertIntoEditor !== false,
      editorRoot: uploadContext.root
    });
  }, { once: true });
  input.click();
}

async function fetchWithTimeout(input, init, timeoutMs, label) {
  var controller = typeof AbortController === 'function' ? new AbortController() : null;
  var timer = 0;
  try {
    if (controller) {
      timer = window.setTimeout(function() {
        controller.abort();
      }, Math.max(1000, Number(timeoutMs) || 15000));
    }
    var nextInit = Object.assign({}, init || {});
    if (controller) nextInit.signal = controller.signal;
    return await fetch(input, nextInit);
  } catch (error) {
    if (error && error.name === 'AbortError') {
      throw new Error((label || '请求') + '超时');
    }
    throw error;
  } finally {
    if (timer) window.clearTimeout(timer);
  }
}

async function uploadLocalFolderAsset(file, plan, context) {
  var rootUri = String(context && context.rootUri || '').trim() || currentRootUri();
  var documentId = String(
    plan && plan.targetDocumentId
    || context && context.documentId
    || ''
  ).trim();
  var hasFolderTarget = plan && Object.prototype.hasOwnProperty.call(plan, 'targetRelativePath');
  var uploadIntent = String(
    plan && plan.uploadIntent
    || (hasFolderTarget ? 'filetree.folder.drop' : 'editor.markdown.attach')
  ).trim();
  if (!rootUri || !uploadIntent) {
    throw new Error('本地 Markdown 上传缺少 rootUri 或 documentId');
  }
  var localForm = new FormData();
  localForm.append('file', file);
  localForm.append('rootUri', rootUri);
  localForm.append('uploadIntent', uploadIntent);
  if (documentId) localForm.append('documentId', documentId);
  if (hasFolderTarget) localForm.append('targetRelativePath', String(plan.targetRelativePath || ''));
  localForm.append('kind', file && String(file.type || '').indexOf('image/') === 0 ? 'image' : 'attachment');
  var localResponse = await fetchWithTimeout('/api/local-folder/assets/upload', {
    method: 'POST',
    credentials: 'include',
    body: localForm
  }, Number(context && context.timeoutMs) || 15000, '本地上传');
  var localPayload = await localResponse.json().catch(function() { return null; });
  if (!localResponse.ok || !localPayload || !localPayload.asset) {
    throw new Error(localPayload && localPayload.error ? localPayload.error : '上传失败');
  }
  return {
    asset: localPayload.asset,
    documentId: documentId
  };
}

async function uploadMediaAsset(file, plan, context) {
  var form = new FormData();
  form.append('file', file);
  form.append('workspaceId', plan && plan.workspaceId || '');
  form.append('documentId', plan && plan.targetDocumentId || '');
  if (plan && plan.targetMindmapId) form.append('mindmapId', plan.targetMindmapId);
  var response = await fetchWithTimeout('/api/media/upload', {
    method: 'POST',
    credentials: 'include',
    body: form
  }, Number(context && context.timeoutMs) || 15000, '上传');
  var payload = await response.json().catch(function() { return null; });
  if (!response.ok || !payload || !payload.asset) {
    throw new Error(payload && payload.error ? payload.error : '上传失败');
  }
  return {
    asset: payload.asset
  };
}

async function insertUploadedAssetIntoEditor(asset, targetRoot, deps) {
  deps = deps || {};
  var editorRoot = targetRoot instanceof HTMLElement
    ? targetRoot.querySelector('.editor-surface .ProseMirror')
    : document.querySelector('.editor-surface .ProseMirror');
  if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot;
  var editor = editorRoot && editorRoot.editor;
  if (!editor || !editor.chain) {
    document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false');
    document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'editor_unavailable');
    return false;
  }
  var title = typeof deps.uploadedAssetTitle === 'function' ? deps.uploadedAssetTitle(asset) : uploadedAssetTitle(asset);
  var localOpenUrl = typeof deps.localAssetOpenUrl === 'function' ? deps.localAssetOpenUrl(asset, false) : localAssetOpenUrl(asset, false);
  var fallbackUrl = typeof deps.uploadedAssetUrl === 'function' ? deps.uploadedAssetUrl(asset) : uploadedAssetUrl(asset);
  var url = localOpenUrl || fallbackUrl;
  var type = typeof deps.uploadedAssetType === 'function' ? deps.uploadedAssetType(asset) : uploadedAssetType(asset);
  var assetId = String(asset && asset.id || '').trim();
  var sizeLabel = typeof deps.uploadedFileSize === 'function' ? deps.uploadedFileSize(asset) : uploadedFileSize(asset);
  try {
    if (type === 'image' && url) {
      return editor.chain().focus().setImage({ src: url, alt: title, title: title }).run() === true;
    }
    var isLocalAsset = typeof deps.isLocalUploadedAsset === 'function' ? deps.isLocalUploadedAsset(asset) : isLocalUploadedAsset(asset);
    var userId = '';
    var buildOnlyOfficeAssetOpenUrl = typeof deps.buildOnlyOfficeAssetOpenUrl === 'function' ? deps.buildOnlyOfficeAssetOpenUrl : function() { return ''; };
    var onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
    if (onlyOfficeUrl && assetId) {
      userId = typeof deps.fetchCurrentOnlyOfficeUserId === 'function' ? await deps.fetchCurrentOnlyOfficeUserId() : '';
      onlyOfficeUrl = buildOnlyOfficeAssetOpenUrl(asset, userId);
    }
    var href = onlyOfficeUrl || url;
    if (href) {
      var storedHref = onlyOfficeUrl
        ? (isLocalAsset ? onlyOfficeUrl : deps.buildOnlyOfficeOpenPath({
            fileUrl: '',
            fileName: title,
            fileType: deps.inferOnlyOfficeFileType(title, asset && asset.mime_type) || 'docx',
            assetId: assetId,
            documentId: String(asset && (asset.document_id || asset.documentId) || deps.currentDocumentId() || '').trim(),
            userId: userId || '',
            mode: 'view'
          }))
        : href;
      var inserted = editor.chain().focus().insertContent([
        {
          type: 'paragraph',
          content: [{
            type: 'text',
            text: title,
            marks: [{
              type: 'link',
              attrs: {
                href: storedHref,
                target: '_blank',
                rel: 'noopener noreferrer nofollow',
                class: typeof deps.uploadedAttachmentClass === 'function' ? deps.uploadedAttachmentClass(asset) : uploadedAttachmentClass(asset)
              }
            }]
          }]
        },
        { type: 'paragraph' }
      ]).focus('end').run() === true;
      if (inserted) {
        document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'true');
        document.documentElement.setAttribute('data-mnote-last-upload-asset-id', assetId || '');
        document.documentElement.removeAttribute('data-mnote-last-upload-insert-error');
        dispatchUploadedEditorChange(editorRoot, editor, deps);
        await persistUploadedEditorChange(editor, asset, deps);
      } else {
        document.documentElement.setAttribute('data-mnote-last-upload-inserted', 'false');
        document.documentElement.setAttribute('data-mnote-last-upload-insert-error', 'insert_content_failed');
      }
      window.setTimeout(function() {
        if (targetRoot instanceof HTMLElement) window.__mnoteLastEditorUploadRoot = targetRoot;
        if (typeof deps.enhanceEditorAttachmentLinks === 'function') deps.enhanceEditorAttachmentLinks();
        var cssEscape = typeof deps.cssEscape === 'function' ? deps.cssEscape : function(value) { return String(value || '').replace(/"/g, '\\"'); };
        var selector = assetId
          ? '.editor-surface .ProseMirror a[href*="' + cssEscape(assetId) + '"]'
          : '.editor-surface .ProseMirror a';
        var link = targetRoot instanceof HTMLElement
          ? targetRoot.querySelector(selector)
          : document.querySelector(selector);
        if (link instanceof HTMLElement) {
          link.setAttribute('data-mnote-attachment-link', 'true');
          if (assetId) link.setAttribute('data-asset-id', assetId);
          if (sizeLabel) link.setAttribute('data-file-size', sizeLabel);
        }
      }, 0);
      return inserted;
    }
  } catch (error) {
    console.warn('[mnote upload] insert uploaded asset failed', error);
  }
  return false;
}

async function uploadFileToMediaAsset(file, plan, options, deps) {
  deps = deps || {};
  options = options || {};
  var getCurrentSourceKind = typeof deps.currentSourceKind === 'function' ? deps.currentSourceKind : function() { return ''; };
  var getCurrentRootUri = typeof deps.currentRootUri === 'function' ? deps.currentRootUri : currentRootUri;
  var getCurrentDocumentId = typeof deps.currentDocumentId === 'function' ? deps.currentDocumentId : function() { return ''; };
  var resolveEditorRoot = typeof deps.editorRootFromUploadOptions === 'function'
    ? deps.editorRootFromUploadOptions
    : function(nextOptions) { return editorRootFromUploadOptions(nextOptions, deps); };
  var insertIntoEditor = typeof deps.insertUploadedAssetIntoEditor === 'function'
    ? deps.insertUploadedAssetIntoEditor
    : function(asset, targetRoot) { return insertUploadedAssetIntoEditor(asset, targetRoot, deps); };
  var dispatch = typeof deps.dispatchEvent === 'function'
    ? deps.dispatchEvent
    : function(event) { return window.dispatchEvent(event); };
  var EventCtor = typeof deps.CustomEvent === 'function' ? deps.CustomEvent : window.CustomEvent;
  if (getCurrentSourceKind() === 'local_folder') {
    var rootUri = getCurrentRootUri();
    var documentId = String(plan && plan.targetDocumentId || getCurrentDocumentId() || '').trim();
    var localResult = await uploadLocalFolderAsset(file, plan, {
      rootUri: rootUri,
      documentId: documentId,
      timeoutMs: 15000
    });
    var localAsset = localResult && localResult.asset ? localResult.asset : null;
    if (!localAsset) {
      throw new Error('上传失败');
    }
    if (options.insertIntoEditor) {
      await insertIntoEditor(localAsset, resolveEditorRoot(options));
    }
    if (typeof deps.refreshLocalFolderSidebarSnapshot === 'function') {
      void deps.refreshLocalFolderSidebarSnapshot();
    }
    dispatch(new EventCtor('wolai:local-assets-changed', {
      detail: { docId: documentId, asset: localAsset, assetIds: [localAsset.id] }
    }));
    return localAsset;
  }
  var mediaResult = await uploadMediaAsset(file, plan, { timeoutMs: 15000 });
  var mediaAsset = mediaResult && mediaResult.asset ? mediaResult.asset : null;
  if (!mediaAsset) {
    throw new Error('上传失败');
  }
  if (typeof deps.appendUploadedAssetRow === 'function') {
    deps.appendUploadedAssetRow(mediaAsset, plan && plan.targetDocumentId);
  }
  if (options.insertIntoEditor) {
    await insertIntoEditor(mediaAsset, resolveEditorRoot(options));
  }
  dispatch(new EventCtor('wolai:assets-changed', {
    detail: {
      docId: plan && plan.targetDocumentId,
      asset: mediaAsset,
      assetIds: [mediaAsset.id]
    }
  }));
  return mediaAsset;
}

async function uploadFilesWithResolvedTarget(files, detail, options, deps) {
  deps = deps || {};
  var resolveFileTreeUploadTarget = typeof deps.resolveFileTreeUploadTarget === 'function' ? deps.resolveFileTreeUploadTarget : null;
  var uploadFileToMediaAsset = typeof deps.uploadFileToMediaAsset === 'function' ? deps.uploadFileToMediaAsset : null;
  if (!resolveFileTreeUploadTarget || !uploadFileToMediaAsset) {
    throw new Error('上传运行时缺少目标解析或上传函数');
  }
  var list = Array.from(files || []).filter(Boolean);
  if (!list.length) return [];
  var plan = await resolveFileTreeUploadTarget(detail || {});
  var uploaded = [];
  var errors = [];
  for (var i = 0; i < list.length; i += 1) {
    try {
      uploaded.push(await uploadFileToMediaAsset(list[i], plan, options || {}));
    } catch (error) {
      errors.push(list[i].name + ': ' + (error && error.message ? error.message : '上传失败'));
    }
  }
  if (errors.length) {
    var message = '部分文件上传失败：\n' + errors.slice(0, 6).join('\n') + (errors.length > 6 ? '\n...' : '');
    if (typeof deps.alert === 'function') {
      deps.alert(message);
    } else {
      window.alert(message);
    }
  }
  return uploaded;
}

function localAssetOpenUrl(asset, download, context) {
  if (!isLocalUploadedAsset(asset)) return '';
  var rootUri = String(context && context.rootUri || asset && (asset.rootUri || asset.root_uri) || '').trim() || currentRootUri();
  var rootRelativePath = String(asset && (asset.rootRelativePath || asset.root_relative_path) || '').trim();
  if (!rootUri || !rootRelativePath) return '';
  var url = new URL('/api/local-folder/files/open', window.location.origin);
  url.searchParams.set('rootUri', rootUri);
  url.searchParams.set('path', rootRelativePath);
  if (download) url.searchParams.set('download', 'true');
  return url.toString();
}

function uploadedAssetType(asset) {
  return String(asset && (asset.asset_type || asset.assetType || asset.mime_type || '') || '').trim();
}

function fileTreeIconKindForFileName(fileName) {
  var name = String(fileName || '').trim().toLowerCase();
  var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
  if (['doc', 'docx', 'odt', 'rtf', 'ppt', 'pptx', 'odp', 'xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'office';
  if (ext === 'md' || ext === 'markdown') return 'markdown';
  if (ext === 'pdf') return 'pdf';
  if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'].indexOf(ext) >= 0) return 'image';
  return '';
}

function isLocalUploadedAsset(asset) {
  var id = String(asset && asset.id || '').trim();
  return String(asset && asset.sourceKind || '').trim() === 'local_folder' || id.indexOf('local:asset:') === 0;
}

function uploadedAssetExtension(asset) {
  var match = uploadedAssetTitle(asset).toLowerCase().match(/\.([a-z0-9]+)$/);
  return match ? match[1] : '';
}

function isNonOfficeAttachmentName(name, ext) {
  var codeFileNames = [
    '.dockerignore', '.editorconfig', '.env', '.eslintrc', '.gitattributes', '.gitignore', '.npmrc', '.prettierrc',
    'dockerfile', 'makefile', 'cmakelists.txt', 'gemfile', 'rakefile', 'procfile'
  ];
  return [
    'pdf', 'toml', 'json', 'yaml', 'yml', 'md', 'markdown', 'txt', 'ini', 'env', 'xml', 'html', 'htm', 'css', 'scss',
    'less', 'js', 'jsx', 'ts', 'tsx', 'mjs', 'cjs', 'py', 'rs', 'go', 'java', 'c', 'cpp', 'h', 'hpp', 'cs',
    'php', 'rb', 'sh', 'bash', 'zsh', 'sql', 'lock', 'log', 'vue', 'svelte', 'astro', 'jsonc', 'json5',
    'mts', 'cts', 'lua', 'dart', 'kt', 'kts', 'swift', 'scala', 'gradle', 'groovy', 'clj',
    'ex', 'exs', 'erl', 'hrl', 'fs', 'fsx', 'r', 'jl', 'm', 'mm', 'pl', 'pm', 'ps1', 'bat', 'cmd',
    'psm1', 'psd1', 'dockerfile', 'containerfile', 'proto', 'graphql', 'gql', 'prisma', 'tf', 'tfvars',
    'hcl', 'nix', 'cmake', 'bazel', 'bzl', 'properties', 'conf', 'cfg', 'config', 'service', 'desktop',
    'gitignore', 'gitattributes', 'editorconfig', 'npmrc', 'prettierrc', 'eslintrc'
  ].indexOf(ext) >= 0 || codeFileNames.indexOf(name) >= 0;
}

function attachmentExtensionFromFileName(fileName) {
  var name = String(fileName || '').trim().toLowerCase();
  return name.indexOf('.') >= 0 ? name.split('.').pop() : '';
}

function isPdfAttachmentFileName(fileName) {
  return attachmentExtensionFromFileName(fileName) === 'pdf';
}

function isCodeAttachmentFileName(fileName) {
  var name = String(fileName || '').trim().toLowerCase();
  var ext = attachmentExtensionFromFileName(name);
  return ext !== 'pdf' && isNonOfficeAttachmentName(name, ext);
}

function inferCodeAttachmentLanguage(fileName) {
  var name = String(fileName || '').trim().toLowerCase();
  var ext = attachmentExtensionFromFileName(name);
  var byName = {
    'dockerfile': 'dockerfile',
    'makefile': 'makefile',
    'cmakelists.txt': 'cmake',
    '.gitignore': 'gitignore',
    '.gitattributes': 'gitattributes',
    '.editorconfig': 'ini',
    '.env': 'dotenv'
  };
  if (byName[name]) return byName[name];
  var byExt = {
    bash: 'bash', bat: 'batch', c: 'c', cjs: 'javascript', cmd: 'batch', conf: 'text', cpp: 'cpp',
    cs: 'csharp', css: 'css', cts: 'typescript', dart: 'dart', dockerfile: 'dockerfile', env: 'dotenv',
    go: 'go', gql: 'graphql', gradle: 'groovy', graphql: 'graphql', h: 'c', hcl: 'hcl', hpp: 'cpp',
    htm: 'html', html: 'html', ini: 'ini', java: 'java', js: 'javascript', json: 'json', json5: 'json',
    jsonc: 'jsonc', jsx: 'javascript', kt: 'kotlin', kts: 'kotlin', less: 'less', log: 'text',
    lua: 'lua', m: 'objective-c', markdown: 'markdown', md: 'markdown', mjs: 'javascript',
    mts: 'typescript', nix: 'nix', php: 'php', pl: 'perl', pm: 'perl', prisma: 'prisma',
    proto: 'protobuf', ps1: 'powershell', py: 'python', r: 'r', rb: 'ruby', rs: 'rust',
    scss: 'scss', sh: 'bash', sql: 'sql', svelte: 'svelte', swift: 'swift', tf: 'terraform',
    tfvars: 'terraform', toml: 'toml', ts: 'typescript', tsx: 'typescript', txt: 'text',
    vue: 'vue', xml: 'xml', yaml: 'yaml', yml: 'yaml', zsh: 'bash'
  };
  return byExt[ext] || 'text';
}

function attachmentClassForFileName(fileName) {
  var name = String(fileName || '').trim().toLowerCase();
  var ext = name.indexOf('.') >= 0 ? name.split('.').pop() : '';
  if (['doc', 'docx', 'odt', 'rtf'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-word';
  if (['ppt', 'pptx', 'odp'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-ppt';
  if (['xls', 'xlsx', 'ods', 'csv'].indexOf(ext) >= 0) return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-sheet';
  if (ext === 'pdf') return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-pdf';
  if (isNonOfficeAttachmentName(name, ext)) {
    return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-code';
  }
  return 'mnote-uploaded-attachment-row mnote-uploaded-attachment-file';
}

function uploadedAttachmentClass(asset) {
  return attachmentClassForFileName(uploadedAssetTitle(asset));
}

function uploadedFileSize(asset) {
  var size = Number(asset && (asset.file_size || asset.fileSize) || 0);
  if (!Number.isFinite(size) || size <= 0) return '';
  if (size >= 1024 * 1024) return (size / 1024 / 1024).toFixed(size >= 10 * 1024 * 1024 ? 1 : 2) + ' MB';
  if (size >= 1024) return (size / 1024).toFixed(size >= 100 * 1024 ? 0 : 2) + ' KB';
  return String(Math.round(size)) + ' B';
}

window.__mnoteLocalUploadRuntime = {
  uploadedAssetTitle: uploadedAssetTitle,
  uploadedAssetUrl: uploadedAssetUrl,
  resolveEditorUploadContext: resolveEditorUploadContext,
  editorRootFromUploadOptions: editorRootFromUploadOptions,
  openEditorUploadFilePicker: openEditorUploadFilePicker,
  fetchWithTimeout: fetchWithTimeout,
  uploadLocalFolderAsset: uploadLocalFolderAsset,
  uploadMediaAsset: uploadMediaAsset,
  insertUploadedAssetIntoEditor: insertUploadedAssetIntoEditor,
  uploadFileToMediaAsset: uploadFileToMediaAsset,
  uploadFilesWithResolvedTarget: uploadFilesWithResolvedTarget,
  localAssetOpenUrl: localAssetOpenUrl,
  uploadedAssetType: uploadedAssetType,
  fileTreeIconKindForFileName: fileTreeIconKindForFileName,
  isLocalUploadedAsset: isLocalUploadedAsset,
  uploadedAssetExtension: uploadedAssetExtension,
  isNonOfficeAttachmentName: isNonOfficeAttachmentName,
  attachmentExtensionFromFileName: attachmentExtensionFromFileName,
  isPdfAttachmentFileName: isPdfAttachmentFileName,
  isCodeAttachmentFileName: isCodeAttachmentFileName,
  inferCodeAttachmentLanguage: inferCodeAttachmentLanguage,
  attachmentClassForFileName: attachmentClassForFileName,
  uploadedAttachmentClass: uploadedAttachmentClass,
  uploadedFileSize: uploadedFileSize
};
