import { apply as _ } from "@scalar/json-magic/diff";
import { deepClone as A } from "@scalar/workspace-store/helpers/deep-clone";
import * as i from "monaco-editor";
import { ref as ae, shallowRef as L, computed as E, toValue as h, watch as ge, markRaw as pe } from "vue";
import { createJsonModel as V } from "../helpers/json/create-json-model.js";
import { ensureJsonPointerLinkSupport as he } from "../helpers/json/json-pointer-links.js";
import { parseJsonPointerPath as ve } from "../helpers/json/json-pointer-path.js";
import { rangeToWholeLine as Ce } from "../helpers/range-to-whole-line.js";
function Je(se) {
  const { baseDocument: ie, resolvedDocument: P, conflicts: w, onApplyChanges: le, onError: F } = se, f = ae([]), S = L([]), Y = L([]), z = L([]), H = ae(-1), M = L(), N = L(), v = L();
  let b, O;
  const x = [];
  let J = [];
  const g = E(() => h(w).map((s) => {
    const r = s[0], d = s[1];
    let u = r[0].path;
    for (const m of r)
      m.path.length < u.length && (u = m.path);
    for (const m of d)
      m.path.length < u.length && (u = m.path);
    return {
      local: {
        path: u,
        changes: r,
        type: r[0].type
      },
      remote: {
        path: u,
        changes: d,
        type: d[0].type
      }
    };
  })), re = E(
    () => _(
      A(h(P)),
      h(w).flatMap((o) => o[0])
    )
  ), ce = E(
    () => _(
      A(h(P)),
      h(w).flatMap((o) => o[1])
    )
  ), U = E(() => f.value.filter((o) => o === "idle").length);
  ge(
    g,
    (o) => {
      f.value = o.map((s, r) => f.value[r] ?? "idle");
    },
    { immediate: !0 }
  );
  const j = (o, s) => {
    if (!o || !s)
      return;
    const r = o.getModifiedEditor(), d = new i.Range(s.startLineNumber, s.startColumn, s.endLineNumber, s.endColumn);
    r.setSelection(d), r.revealRangeNearTop(d);
  };
  return {
    init: (o) => {
      const s = h(ie), r = h(P), d = re.value, u = ce.value, m = i.editor.createModel(JSON.stringify(r, null, 2), "json"), c = V(JSON.stringify(d, null, 2)), D = i.editor.createModel(JSON.stringify(r, null, 2), "json"), I = V(JSON.stringify(u, null, 2)), $ = i.editor.createModel(JSON.stringify(s, null, 2), "json"), p = V(JSON.stringify(r, null, 2));
      J = [
        m,
        c.model,
        D,
        I.model,
        $,
        p.model
      ];
      const q = i.editor.createDiffEditor(o.local, {
        originalEditable: !1,
        readOnly: !0,
        automaticLayout: !0,
        renderSideBySide: !1
      });
      q.setModel({
        original: m,
        modified: c.model
      }), M.value = q;
      const G = i.editor.createDiffEditor(o.remote, {
        originalEditable: !1,
        readOnly: !0,
        automaticLayout: !0,
        renderSideBySide: !1
      });
      G.setModel({
        original: D,
        modified: I.model
      }), N.value = G;
      const T = i.editor.createDiffEditor(o.result, {
        originalEditable: !1,
        readOnly: !1,
        renderSideBySide: !1,
        automaticLayout: !0
      });
      T.setModel({
        original: $,
        modified: p.model
      });
      const C = T.getModifiedEditor();
      C.updateOptions({ codeLens: !0 });
      const de = async (t) => {
        const e = await p.getRangeFromPath(t);
        if (!e)
          return;
        const n = new i.Range(e.startLineNumber, e.startColumn, e.endLineNumber, e.endColumn);
        C.setSelection(n), C.revealRangeNearTop(n);
      };
      O = he(async (t) => {
        const e = ve(t);
        e && await de(e);
      }).dispose;
      let K = [], W = 0;
      const Q = "scalar.conflict.applyCurrent", X = "scalar.conflict.applyRemote", Z = "scalar.conflict.ignore", ue = C.addCommand(0, () => {
      }) ?? "scalar.conflict.status.noop", me = (t) => {
        const e = t.startLineNumber, n = t.endLineNumber;
        if (e === n)
          return [
            {
              range: t,
              options: {
                isWholeLine: !0,
                className: "json-focus-highlight-box-single"
              }
            }
          ];
        const a = new i.Range(e, 1, e, t.endColumn), l = new i.Range(e + 1, 1, Math.max(e + 1, n - 1), 1), R = new i.Range(n, 1, n, t.endColumn), y = [
          {
            range: a,
            options: {
              isWholeLine: !0,
              className: "json-focus-highlight-box-top"
            }
          },
          {
            range: R,
            options: {
              isWholeLine: !0,
              className: "json-focus-highlight-box-bottom"
            }
          }
        ];
        return n - e > 1 && y.push({
          range: l,
          options: {
            isWholeLine: !0,
            className: "json-focus-highlight-box-middle"
          }
        }), y;
      }, k = () => {
        b?.dispose(), b = i.languages.registerCodeLensProvider("json", {
          provideCodeLenses: (t) => t.uri.toString() !== p.model.uri.toString() ? { lenses: [], dispose: () => {
          } } : {
            lenses: S.value.flatMap((e) => {
              const n = new i.Range(
                e.range.startLineNumber,
                1,
                e.range.startLineNumber,
                1
              );
              return [
                {
                  range: n,
                  command: {
                    id: ue,
                    title: `Status: ${f.value[e.index] ?? "idle"}`
                  }
                },
                {
                  range: n,
                  command: {
                    id: Q,
                    title: "Accept Current",
                    arguments: [e.index]
                  }
                },
                {
                  range: n,
                  command: {
                    id: X,
                    title: "Accept Remote",
                    arguments: [e.index]
                  }
                },
                {
                  range: n,
                  command: {
                    id: Z,
                    title: "Ignore",
                    arguments: [e.index]
                  }
                }
              ];
            }),
            dispose: () => {
            }
          }
        });
      }, B = async (t, e, n) => {
        const { model: a } = t, l = new i.Range(1, 1, 1, a.getLineMaxColumn(1));
        let R = null;
        for (let y = n.length; y >= 0; y -= 1) {
          const fe = n.slice(0, y), ne = await t.getRangeFromPath(fe);
          if (ne) {
            R = Ce(a, ne);
            break;
          }
        }
        return {
          index: e,
          path: n,
          range: pe(R ?? l)
        };
      }, ee = async () => {
        const [t, e, n] = await Promise.all([
          Promise.all(
            g.value.map(
              (a, l) => B(p, l, a.local.path)
            )
          ),
          Promise.all(
            g.value.map(
              (a, l) => B(c, l, a.local.path)
            )
          ),
          Promise.all(
            g.value.map(
              (a, l) => B(I, l, a.local.path)
            )
          )
        ]);
        S.value = t.filter((a) => !!a), Y.value = e.filter((a) => !!a), z.value = n.filter((a) => !!a), K = C.deltaDecorations(
          K,
          S.value.flatMap((a) => me(a.range))
        ), k();
      }, oe = (t, e) => {
        f.value[t] = e, k();
      }, te = async (t, e) => {
        const n = g.value[t];
        if (!n)
          return;
        let a = null;
        try {
          a = JSON.parse(p.model.getValue());
        } catch {
          return;
        }
        const l = e === "local" ? n.local.changes : n.remote.changes, R = _(A(a), l);
        W += 1, p.model.setValue(JSON.stringify(R, null, 2)), oe(t, e), await ee();
      };
      x.push(
        i.editor.registerCommand(Q, (t, e) => {
          te(e, "local");
        })
      ), x.push(
        i.editor.registerCommand(X, (t, e) => {
          te(e, "remote");
        })
      ), x.push(
        i.editor.registerCommand(Z, (t, e) => {
          oe(e, "ignore");
        })
      ), ee(), C.onDidChangeModelContent((t) => {
        if (W > 0) {
          W -= 1;
          return;
        }
        let e = !1;
        for (const n of t.changes) {
          const a = S.value.filter(
            (l) => i.Range.areIntersectingOrTouching(n.range, l.range)
          );
          a.length && a.forEach((l) => {
            f.value[l.index] !== "manual" && (f.value[l.index] = "manual", e = !0);
          });
        }
        e && k();
      }), v.value = T;
    },
    dispose: () => {
      M.value?.dispose(), M.value = void 0, N.value?.dispose(), N.value = void 0, v.value?.dispose(), v.value = void 0;
      for (const o of J)
        o.dispose();
      J = [], b?.dispose(), b = void 0, O?.(), O = void 0, x.forEach((o) => o.dispose()), x.length = 0;
    },
    conflictsLeft: U,
    goToNextConflict: () => {
      const o = g.value.map((c, D) => D).filter((c) => f.value[c] === "idle");
      if (!o.length)
        return;
      const s = o.find((c) => c > H.value) ?? o[0];
      if (s === void 0 || !g.value[s])
        return;
      const d = Y.value.find((c) => c.index === s)?.range, u = z.value.find((c) => c.index === s)?.range, m = S.value.find((c) => c.index === s)?.range;
      j(M.value, d), j(N.value, u), j(v.value, m), v.value?.getModifiedEditor().focus(), H.value = s;
    },
    applyResolvedConflicts: () => {
      if (U.value > 0) {
        F?.("You have conflicts left");
        return;
      }
      const o = v.value?.getModel()?.modified;
      if (!o)
        return;
      let s = null;
      try {
        s = JSON.parse(o.getValue());
      } catch {
        F?.("You have formatting errors");
        return;
      }
      le(s);
    }
  };
}
export {
  Je as useThreeWayMergeEditor
};
