// Comments in English
import { EditorView, GutterMarker, ViewPlugin, Decoration, gutter, WidgetType } from "@codemirror/view";
import { EditorSelection } from "@codemirror/state";
import { syntaxTree } from "@codemirror/language";
import type { Range } from "@codemirror/state";
import { RangeSetBuilder } from "@codemirror/state";

function fenceNodeAt(state: EditorView["state"], pos: number) {
  const tree = syntaxTree(state);
  let found: { from: number; to: number } | null = null;
  tree.iterate({ from: pos, to: pos, enter(n) { if (n.name === "FencedCode" && n.from <= pos && n.to >= pos) found = { from: n.from, to: n.to }; }});
  return found;
}

function fencedBlocksInSelection(state: EditorView["state"]) {
  const blocks: Array<{from:number; to:number}> = [];
  const seen = new Set<string>();
  for (const r of state.selection.ranges) {
    const posList = [r.from, r.to, state.selection.main.head];
    for (const p of posList) {
      const n = fenceNodeAt(state, p);
      if (n) { const key = `${n.from}:${n.to}`; if (!seen.has(key)) { seen.add(key); blocks.push(n); } }
    }
  }
  if (!blocks.length) {
    const tree = syntaxTree(state);
    for (const r of state.selection.ranges) {
      tree.iterate({ from: r.from, to: r.to, enter(n) { if (n.name === "FencedCode") { const key = `${n.from}:${n.to}`; if (!seen.has(key)) { seen.add(key); blocks.push({from:n.from,to:n.to}); } } }});
    }
  }
  return blocks;
}

export function toggleFenceBlock(view: EditorView): boolean {
  const { state } = view;
  const blocks = fencedBlocksInSelection(state);
  if (blocks.length) {
    type Change = { from:number; to:number; insert:string };
    const changes: Change[] = [];
    for (const b of blocks) {
      const openL = state.doc.lineAt(b.from);
      const closeL = state.doc.lineAt(b.to);
      const delOpenFrom = openL.from;
      const delOpenTo   = Math.min(openL.to + 1, state.doc.length);
      changes.push({ from: delOpenFrom, to: delOpenTo, insert: "" });
      const beforeClose = Math.max(0, closeL.from - 1);
      const hasPrevNL = state.doc.sliceString(beforeClose, closeL.from) === "\n";
      const delCloseFrom = hasPrevNL ? beforeClose : closeL.from;
      const delCloseTo   = closeL.to;
      changes.push({ from: delCloseFrom, to: delCloseTo, insert: "" });
    }
    changes.sort((a,b) => (b.from - a.from) || (b.to - a.to));
    view.dispatch({ changes, userEvent: "input.toggleFence" });
    return true;
  }

  const tr = state.changeByRange(range => {
    let from = range.from, to = range.to;
    const doc = state.doc;
    const selText = doc.sliceString(from, to);
    const open = "```"; const close = "```";
    if (from !== to) {
      const insert = open + "\n" + selText + "\n" + close + "\n";
      return { changes: { from, to, insert }, range: EditorSelection.cursor(from + open.length + 1) };
    }
    const line = doc.lineAt(from);
    const onEmptyLine = line.text.trim().length === 0;
    if (onEmptyLine) {
      const insert = open + "\n\n" + close + "\n";
      const start = line.from;
      return { changes: { from: start, to: line.to, insert }, range: EditorSelection.cursor(start + open.length + 1) };
    } else {
      const insert = "\n" + open + "\n\n" + close + "\n";
      const pos = line.to;
      return { changes: { from: pos, to: pos, insert }, range: EditorSelection.cursor(pos + 1 + open.length + 1) };
    }
  });
  if (tr.changes.empty) return false;
  view.dispatch(tr);
  return true;
}

// ---- Code block line numbers (gutter) ----
export function codeBlockLineNumbers() {
  class NumMarker extends GutterMarker { constructor(readonly n: number) { super(); } toDOM() { const el = document.createElement("div"); el.className = "cm-codeblock-lno"; el.textContent = String(this.n); return el; } }
  function markers(view: EditorView) {
    const b = new RangeSetBuilder<GutterMarker>();
    const tree = syntaxTree(view.state);
    const { from, to } = view.viewport;
    tree.iterate({ from, to, enter(node) {
      if (node.name === "FencedCode") {
        const startLine = view.state.doc.lineAt(node.from);
        const endLine   = view.state.doc.lineAt(node.to);
        let num = 0;
        for (let i = startLine.number + 1; i <= endLine.number - 1; i++) {
          num += 1; const line = view.state.doc.line(i); b.add(line.from, line.from, new NumMarker(num));
        }
      }
    }});
    return b.finish();
  }
  return [
    gutter({ class: "cm-codeblock-gutter", markers, lineMarkerChange(update) { return update.docChanged || update.viewportChanged; } }),
    EditorView.theme({
      ".cm-gutters": { userSelect: "none" },
      ".cm-codeblock-gutter": { width: "3ch" },
      ".cm-codeblock-gutter .cm-codeblock-lno": { textAlign: "right", paddingRight: "0.5ch", opacity: 0.7, fontVariantNumeric: "tabular-nums" }
    })
  ];
}

// ---- Copy button per fenced block ----
export function codeBlockCopyButtons() {
  class CopyBtnWidget extends WidgetType {
    constructor(readonly from: number, readonly to: number) { super(); }
    eq(other: CopyBtnWidget) { return this.from === other.from && this.to === other.to; }
    toDOM(view: EditorView) {
      const btn = document.createElement("button");
      btn.className = "cm-codeblock-copybtn";
      btn.type = "button";
      btn.title = "Copiar código";
      btn.textContent = "⧉";
      btn.onclick = async (e) => {
        e.preventDefault();
        const doc = view.state.doc;
        const startL = doc.lineAt(this.from);
        const endL   = doc.lineAt(this.to);
        const startInner = startL.to + 1;
        const endInner   = endL.from - 1;
        const code = doc.sliceString(startInner, endInner);
        try { await navigator.clipboard.writeText(code); } catch {}
      };
      return btn;
    }
    ignoreEvent() { return false; }
  }

  const plugin = ViewPlugin.fromClass(class {
    decorations: DecorationSet;
    constructor(view: EditorView) { this.decorations = this.build(view); }
    update(u: any) { if (u.docChanged || u.viewportChanged) this.decorations = this.build(u.view); }
    build(view: EditorView) {
      const decos: Range<Decoration>[] = [];
      const tree = syntaxTree(view.state);
      const { from, to } = view.viewport;
      tree.iterate({ from, to, enter(node) {
        if (node.name === "FencedCode") {
          const line = view.state.doc.lineAt(node.from);
          decos.push(Decoration.widget({ widget: new CopyBtnWidget(node.from, node.to), side: 1 }).range(line.to));
        }
      }});
      return Decoration.set(decos, true);
    }
  }, { decorations: (v) => v.decorations });

  const theme = EditorView.theme({
    ".cm-line": { position: "relative" },
    ".cm-codeblock-copybtn": { position: "absolute", right: "0.25rem", top: "0.15rem", fontSize: "0.8rem", padding: "0.1rem 0.3rem", border: "none", borderRadius: "4px", cursor: "pointer", opacity: 0.7 },
    ".cm-codeblock-copybtn:hover": { opacity: 1 }
  });

  return [plugin, theme];
}

// ---- Fallback DOM for Mod+` ----
export function fenceKeydownFallback() {
  return EditorView.domEventHandlers({
    keydown(event, view) {
      const isMod = event.metaKey || event.ctrlKey; if (!isMod) return false;
      const k = (event as KeyboardEvent).key; const looksLikeBacktick = (k === "`" || k === "Dead");
      if (looksLikeBacktick) { event.preventDefault(); event.stopPropagation(); return toggleFenceBlock(view as EditorView); }
      if (event.altKey && !event.shiftKey && (k.toLowerCase?.() === 'c')) { event.preventDefault(); event.stopPropagation(); return toggleFenceBlock(view as EditorView); }
      return false;
    }
  });
}
