(function () {
  const {
    EditorView, lineNumbers, highlightSpecialChars, drawSelection,
    EditorState, syntaxHighlighting, bracketMatching, foldGutter,
    oneDark, oneDarkHighlightStyle,
    MergeView, unifiedMergeView,
    languages, LanguageDescription,
  } = globalThis.CM;

  const files = window.__FILES__ || [];
  const container = document.getElementById("editor");
  const fileList = document.getElementById("file-list");

  let currentView = null;
  let currentMode = "side";
  let currentFileIndex = 0;

  async function makeExtensions(filename) {
    const match = LanguageDescription.matchFilename(languages, filename || "");
    const lang = match ? await match.load() : null;
    return [
      lineNumbers(),
      highlightSpecialChars(),
      drawSelection(),
      bracketMatching(),
      foldGutter(),
      syntaxHighlighting(oneDarkHighlightStyle),
      ...(lang ? [lang] : []),
      oneDark,
      EditorView.editable.of(false),
      EditorState.readOnly.of(true),
    ];
  }

  function destroyCurrent() {
    if (currentView) { currentView.destroy(); currentView = null; }
    container.replaceChildren();
  }

  async function renderFile(index) {
    destroyCurrent();
    currentFileIndex = index;
    const file = files[index];

    if (file.binary) {
      const placeholder = document.createElement("div");
      placeholder.className = "binary-placeholder";
      placeholder.textContent = "Binary file — not shown";
      container.appendChild(placeholder);
      updateFileListSelection();
      return;
    }

    const exts = await makeExtensions(file.path);

    if (currentMode === "side") {
      currentView = new MergeView({
        a: { doc: file.original, extensions: exts },
        b: { doc: file.modified, extensions: exts },
        parent: container,
        collapseUnchanged: { margin: 3, minSize: 4 },
        gutter: true,
        highlightChanges: true,
      });
    } else {
      currentView = new EditorView({
        doc: file.modified,
        extensions: [
          ...exts,
          unifiedMergeView({ original: file.original, highlightChanges: true, gutter: true, mergeControls: false }),
        ],
        parent: container,
      });
    }

    updateFileListSelection();
  }

  function updateFileListSelection() {
    fileList.querySelectorAll("[data-file-index]").forEach((el) => {
      el.classList.toggle("is-active", Number(el.dataset.fileIndex) === currentFileIndex);
    });
  }

  const resizeHandle = document.createElement("div");
  resizeHandle.className = "sidebar-resize-handle";

  const STATUS_LABEL = { added: "A", modified: "M", deleted: "D", renamed: "R" };

  const validStatuses = new Set(["added", "modified", "deleted", "renamed"]);

  function buildFileList() {
    function buildRawTree() {
      const root = {};
      files.forEach((file, index) => {
        const parts = file.path.split("/");
        let node = root;
        for (let i = 0; i < parts.length - 1; i++) {
          const part = parts[i];
          if (!node[part]) node[part] = { type: "dir", name: part, children: {} };
          node = node[part].children;
        }
        const filename = parts[parts.length - 1];
        node[filename] = { type: "file", name: filename, index, file };
      });
      return root;
    }

    function compact(nodeMap) {
      const result = {};
      for (const [key, child] of Object.entries(nodeMap)) {
        if (child.type === "dir") {
          child.children = compact(child.children);
          let mergedName = key;
          let mergedChildren = child.children;
          while (true) {
            const entries = Object.entries(mergedChildren);
            if (entries.length === 1 && entries[0][1].type === "dir") {
              const [childKey, grandChild] = entries[0];
              mergedName = mergedName + "/" + childKey;
              mergedChildren = grandChild.children;
            } else {
              break;
            }
          }
          result[mergedName] = { type: "dir", name: mergedName, children: mergedChildren };
        } else {
          result[key] = child;
        }
      }
      return result;
    }

    function renderNode(child) {
      const li = document.createElement("li");
      li.className = "file-tree-node " + (child.type === "dir" ? "file-tree-dir" : "file-tree-file");

      if (child.type === "dir") {
        const details = document.createElement("details");
        details.open = true;

        const summary = document.createElement("summary");
        const folderIcon = document.createElement("span");
        folderIcon.className = "file-tree-icon file-tree-icon-folder";
        folderIcon.innerHTML = window.__ICONS__.folder;
        const dirName = document.createElement("span");
        dirName.className = "file-tree-dir-name";
        dirName.textContent = child.name;
        summary.appendChild(folderIcon);
        summary.appendChild(dirName);

        const childUl = document.createElement("ul");
        for (const [, grandChild] of Object.entries(child.children)) {
          childUl.appendChild(renderNode(grandChild));
        }

        details.appendChild(summary);
        details.appendChild(childUl);
        li.appendChild(details);
      } else {
        const btn = document.createElement("button");
        btn.className = "file-tree-file-button";
        btn.dataset.fileIndex = child.index;

        const mainSpan = document.createElement("span");
        mainSpan.className = "file-tree-file-main";
        const fileIcon = document.createElement("span");
        fileIcon.className = "file-tree-icon file-tree-icon-file";
        fileIcon.innerHTML = window.__ICONS__.file;
        const fileName = document.createElement("span");
        fileName.className = "file-tree-file-name";
        fileName.textContent = child.name;
        fileName.title = child.file.path;
        mainSpan.appendChild(fileIcon);
        mainSpan.appendChild(fileName);

        const diffSpan = document.createElement("span");
        diffSpan.className = "file-tree-file-diff";
        const statusClass = validStatuses.has(child.file.status)
          ? `file-tree-file-status-${child.file.status}`
          : "file-tree-file-status-modified";
        const statusSpan = document.createElement("span");
        statusSpan.className = `file-tree-file-status ${statusClass}`;
        statusSpan.textContent = STATUS_LABEL[child.file.status] || "M";
        const addSpan = document.createElement("span");
        addSpan.className = "file-tree-file-additions";
        addSpan.textContent = `+${child.file.additions}`;
        const delSpan = document.createElement("span");
        delSpan.className = "file-tree-file-deletions";
        delSpan.textContent = `-${child.file.deletions}`;
        diffSpan.appendChild(statusSpan);
        if (!child.file.binary) {
          diffSpan.appendChild(addSpan);
          diffSpan.appendChild(delSpan);
        }

        btn.appendChild(mainSpan);
        btn.appendChild(diffSpan);
        li.appendChild(btn);
      }

      return li;
    }

    const rootUl = document.createElement("ul");
    rootUl.className = "file-jump-tree";
    const compacted = compact(buildRawTree());
    for (const [, child] of Object.entries(compacted)) {
      rootUl.appendChild(renderNode(child));
    }

    fileList.replaceChildren(rootUl, resizeHandle);
  }

  const btnSide = document.getElementById("btn-side");
  const btnUnified = document.getElementById("btn-unified");

  if (btnSide) btnSide.addEventListener("click", () => {
    currentMode = "side";
    btnSide.classList.add("active");
    if (btnUnified) btnUnified.classList.remove("active");
    renderFile(currentFileIndex);
  });

  if (btnUnified) btnUnified.addEventListener("click", () => {
    currentMode = "unified";
    btnUnified.classList.add("active");
    if (btnSide) btnSide.classList.remove("active");
    renderFile(currentFileIndex);
  });

  document.addEventListener("keydown", (e) => {
    if (e.altKey && e.key === "ArrowDown") {
      e.preventDefault();
      renderFile(Math.min(currentFileIndex + 1, files.length - 1));
    } else if (e.altKey && e.key === "ArrowUp") {
      e.preventDefault();
      renderFile(Math.max(currentFileIndex - 1, 0));
    }
  });

  fileList.addEventListener("click", (e) => {
    const btn = e.target.closest("[data-file-index]");
    if (btn) renderFile(Number(btn.dataset.fileIndex));
  });

  resizeHandle.addEventListener("mousedown", (e) => {
    e.preventDefault();
    const startX = e.clientX;
    const startWidth = fileList.offsetWidth;
    document.body.style.userSelect = "none";

    const onMouseMove = (e) => {
      const newWidth = Math.min(600, Math.max(120, startWidth + e.clientX - startX));
      fileList.style.width = newWidth + "px";
    };

    const onMouseUp = () => {
      document.body.style.userSelect = "";
      document.removeEventListener("mousemove", onMouseMove);
      document.removeEventListener("mouseup", onMouseUp);
    };

    document.addEventListener("mousemove", onMouseMove);
    document.addEventListener("mouseup", onMouseUp);
  });

  if (files.length > 0) {
    buildFileList();
    renderFile(0);
  }
}());
