import assert from "node:assert/strict";

import {
  createMachineSessionReadinessFieldState,
  createMachineSessionLoadFieldState,
  loadMachineSessionIntoWasmWorkflow,
  machineSessionLoadWorkflowLogLines,
  machineSessionReadinessLogLines,
  readMachineSessionReadinessWorkflow,
  restoreMachineSessionSnapshotWorkflow,
  saveMachineSessionSnapshotWorkflow,
} from "../../../runtime/ui/ini-panel/session-workflow.js";

const loadedSessionFixture = {
  ini: {
    opfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
    wasmPath: "/work/session-machine.ini",
  },
  parameters: {
    opfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
    wasmPath: "/work/session-linuxcnc.var",
    result: "restore_parameters=0",
  },
  toolTable: {
    opfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl",
    wasmPath: "/work/session-tool.tbl",
    result: "tooldata_load=0",
  },
};

const loadWorkflowCalls = [];
const loadWorkflow = await loadMachineSessionIntoWasmWorkflow({
  loadSession: async () => {
    loadWorkflowCalls.push(["loadSession"]);
    return loadedSessionFixture;
  },
});

assert.deepEqual(loadWorkflowCalls, [["loadSession"]]);
assert.deepEqual(loadWorkflow.loadSummary, {
  iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
  iniWasmPath: "/work/session-machine.ini",
  parameterOpfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  parameterWasmPath: "/work/session-linuxcnc.var",
  parameterResult: "restore_parameters=0",
  toolTableOpfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl",
  toolTableWasmPath: "/work/session-tool.tbl",
  toolTableResult: "tooldata_load=0",
});
assert.deepEqual(loadWorkflow.fields, {
  sessionIni: "/work/session-machine.ini",
  sessionParameters: "/work/session-linuxcnc.var",
  sessionToolTable: "/work/session-tool.tbl",
});
assert.deepEqual(createMachineSessionLoadFieldState(loadWorkflow.loadSummary), loadWorkflow.fields);
assert.deepEqual(machineSessionLoadWorkflowLogLines(loadWorkflow.loadSummary), [
  "Loaded machine session into the interpreter WASM filesystem.",
  "INI: linuxcnc/machines/xyzab-tdr/machine.ini -> /work/session-machine.ini",
  "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> /work/session-linuxcnc.var",
  "Parameters: restore_parameters=0",
  "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl -> /work/session-tool.tbl",
  "Tool table: tooldata_load=0",
]);
await assert.rejects(
  () => loadMachineSessionIntoWasmWorkflow({}),
  /machine session loader/,
);

const readySessionReadiness = {
  ready: true,
  phase: "ready",
  missing: [],
  checks: [
    {
      name: "ini",
      path: "linuxcnc/machines/xyzab-tdr/machine.ini",
      ok: true,
      error: null,
    },
    {
      name: "parameters",
      path: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
      ok: true,
      error: null,
    },
    {
      name: "toolTable",
      path: "linuxcnc/machines/xyzab-tdr/tool.tbl",
      ok: true,
      error: null,
    },
  ],
  snapshotCheck: {
    name: "snapshot",
    path: "linuxcnc/sessions/ui-machine-session/ui-machine-session.json",
    ok: true,
    error: null,
  },
};

const readinessWorkflowCalls = [];
const readinessWorkflow = await readMachineSessionReadinessWorkflow({
  readReadiness: async () => {
    readinessWorkflowCalls.push(["readReadiness"]);
    return readySessionReadiness;
  },
});
assert.deepEqual(readinessWorkflowCalls, [["readReadiness"]]);
assert.equal(readinessWorkflow.readiness, readySessionReadiness);
assert.deepEqual(createMachineSessionReadinessFieldState(readySessionReadiness), {
  sessionReadinessPhase: "ready",
  sessionReadinessReady: "ready",
  sessionReadinessMissing: "none",
});
assert.deepEqual(readinessWorkflow.fields, {
  sessionReadinessPhase: "ready",
  sessionReadinessReady: "ready",
  sessionReadinessMissing: "none",
});
assert.deepEqual(machineSessionReadinessLogLines(readySessionReadiness), [
  "Session readiness: ready",
  "ini: linuxcnc/machines/xyzab-tdr/machine.ini -> ok",
  "parameters: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> ok",
  "toolTable: linuxcnc/machines/xyzab-tdr/tool.tbl -> ok",
  "snapshot: linuxcnc/sessions/ui-machine-session/ui-machine-session.json -> ok",
]);
assert.deepEqual(readinessWorkflow.logLines, machineSessionReadinessLogLines(readySessionReadiness));
await assert.rejects(
  () => readMachineSessionReadinessWorkflow({}),
  /machine session readiness reader/,
);

const blockedSessionReadiness = {
  ready: false,
  phase: "blocked",
  missing: ["parameters", "toolTable"],
  checks: [
    {
      name: "parameters",
      path: "linuxcnc/machines/missing/linuxcnc.var",
      ok: false,
      error: "missing file",
    },
    {
      name: "toolTable",
      path: "linuxcnc/machines/missing/tool.tbl",
      ok: false,
      error: "missing file",
    },
  ],
  snapshotCheck: null,
};
assert.deepEqual(createMachineSessionReadinessFieldState(blockedSessionReadiness), {
  sessionReadinessPhase: "blocked",
  sessionReadinessReady: "blocked",
  sessionReadinessMissing: "parameters, toolTable",
});
assert.deepEqual(machineSessionReadinessLogLines(blockedSessionReadiness), [
  "Session readiness: blocked",
  "parameters: linuxcnc/machines/missing/linuxcnc.var -> blocked (missing file)",
  "toolTable: linuxcnc/machines/missing/tool.tbl -> blocked (missing file)",
]);

const calls = [];
const saveWorkflow = await saveMachineSessionSnapshotWorkflow({
  machineId: "xyzab-tdr",
  snapshotId: "ui-machine-session",
  snapshotFilename: "ui-machine-session.json",
  snapshotLabel: "ui-machine-session/ui-machine-session.json",
  iniText: "[EMC]\nMACHINE = xyzab-tdr\n",
  toolTableText: "T2 P7 Z3.125 D1.5 I12 J34 Q4 ;ui session tool\n",
  parameterText: "5161 0.0\n",
  gcodeFilename: "ui-session.ngc",
  gcodeText: "G0 X1.0 Y2.0 (Comment)\nG1 X3.0 Y4.0 F120.0\n",
  metadata: {
    source: "ini-panel",
    workflow: "save-session-snapshot",
    snapshotLabel: "ui-machine-session/ui-machine-session.json",
    defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
  },
  saveMachineTextFiles: async (machineId, files) => {
    calls.push(["saveMachineTextFiles", machineId, files]);
  },
  saveGcodeProgram: async (filename, text) => {
    calls.push(["saveGcodeProgram", filename, text]);
  },
  saveMachineSessionSnapshot: async (snapshotId, machineId, options) => {
    calls.push(["saveMachineSessionSnapshot", snapshotId, machineId, options]);
    return {
      sessionId: snapshotId,
      metadata: options.metadata,
      payload: {
        files: {
          ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
          parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
          toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
          gcode: "linuxcnc/gcode/ui-session.ngc",
        },
      },
    };
  },
});

assert.deepEqual(calls, [
  [
    "saveMachineTextFiles",
    "xyzab-tdr",
    {
      ini: "[EMC]\nMACHINE = xyzab-tdr\n",
      toolTable: "T2 P7 Z3.125 D1.5 I12 J34 Q4 ;ui session tool\n",
      parameters: "5161 0.0\n",
    },
  ],
  [
    "saveGcodeProgram",
    "ui-session.ngc",
    "G0 X1.0 Y2.0 (Comment)\nG1 X3.0 Y4.0 F120.0\n",
  ],
  [
    "saveMachineSessionSnapshot",
    "ui-machine-session",
    "xyzab-tdr",
    {
      filename: "ui-machine-session.json",
      gcodeFilename: "ui-session.ngc",
      metadata: {
        source: "ini-panel",
        workflow: "save-session-snapshot",
        snapshotLabel: "ui-machine-session/ui-machine-session.json",
        defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
      },
    },
  ],
]);

assert.deepEqual(saveWorkflow.summary, {
  snapshotLabel: "ui-machine-session/ui-machine-session.json",
  workflow: "save-session-snapshot",
  defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
  iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
  parameterOpfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  toolTableOpfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl",
  gcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
});

assert.deepEqual(saveWorkflow.fields, {
  sessionIni: "linuxcnc/machines/xyzab-tdr/machine.ini",
  sessionParameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  sessionToolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
  sessionGcode: "linuxcnc/gcode/ui-session.ngc",
  sessionSnapshot: "ui-machine-session/ui-machine-session.json",
});

assert.deepEqual(saveWorkflow.logLines, [
  "Snapshot: ui-machine-session/ui-machine-session.json",
  "Workflow: save-session-snapshot",
  "Default G-code: linuxcnc/gcode/ui-session.ngc",
  "INI: linuxcnc/machines/xyzab-tdr/machine.ini",
  "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl",
  "G-code: linuxcnc/gcode/ui-session.ngc",
]);

const restoreCalls = [];
const restoreWorkflow = await restoreMachineSessionSnapshotWorkflow({
  machineId: "xyzab-tdr",
  snapshotId: "ui-machine-session",
  snapshotFilename: "ui-machine-session.json",
  snapshotLabel: "ui-machine-session/ui-machine-session.json",
  loadMachineSessionSnapshot: async (snapshotId, machineId, options) => {
    restoreCalls.push(["loadMachineSessionSnapshot", snapshotId, machineId, options]);
    return {
      sessionId: snapshotId,
      metadata: {
        workflow: "save-session-snapshot",
        snapshotLabel: "ui-machine-session/ui-machine-session.json",
        defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
      },
      payload: {
        files: {
          ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
          parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
          toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
          gcode: "linuxcnc/gcode/ui-session.ngc",
        },
      },
    };
  },
  loadMachineTextFiles: async (machineId) => {
    restoreCalls.push(["loadMachineTextFiles", machineId]);
    return {
      ini: "[EMC]\nMACHINE = xyzab-tdr\n",
    };
  },
});

assert.deepEqual(restoreCalls, [
  [
    "loadMachineSessionSnapshot",
    "ui-machine-session",
    "xyzab-tdr",
    { filename: "ui-machine-session.json" },
  ],
  ["loadMachineTextFiles", "xyzab-tdr"],
]);

assert.deepEqual(restoreWorkflow.fields, {
  sessionIni: "linuxcnc/machines/xyzab-tdr/machine.ini",
  sessionParameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  sessionToolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
  sessionGcode: "linuxcnc/gcode/ui-session.ngc",
  sessionSnapshot: "ui-machine-session/ui-machine-session.json",
});

assert.deepEqual(restoreWorkflow.logLines, [
  "Snapshot: ui-machine-session/ui-machine-session.json",
  "Workflow: save-session-snapshot",
  "Default G-code: linuxcnc/gcode/ui-session.ngc",
  "INI: linuxcnc/machines/xyzab-tdr/machine.ini",
  "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl",
  "G-code: linuxcnc/gcode/ui-session.ngc",
]);

const restoreReadinessCalls = [];
const restoreReadinessWorkflow = await restoreMachineSessionSnapshotWorkflow({
  machineId: "xyzab-tdr",
  snapshotId: "ui-machine-session",
  snapshotFilename: "ui-machine-session.json",
  snapshotLabel: "ui-machine-session/ui-machine-session.json",
  loadMachineSessionSnapshot: async (snapshotId, machineId, options) => {
    restoreReadinessCalls.push(["loadMachineSessionSnapshot", snapshotId, machineId, options]);
    return {
      sessionId: snapshotId,
      metadata: {
        workflow: "save-session-snapshot",
        snapshotLabel: "ui-machine-session/ui-machine-session.json",
        defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
      },
      payload: {
        files: {
          ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
          parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
          toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
          gcode: "linuxcnc/gcode/ui-session.ngc",
        },
      },
    };
  },
  loadMachineTextFiles: async (machineId) => {
    restoreReadinessCalls.push(["loadMachineTextFiles", machineId]);
    return {
      ini: "[EMC]\nMACHINE = xyzab-tdr\n",
    };
  },
  readReadiness: async () => {
    restoreReadinessCalls.push(["readReadiness"]);
    return readySessionReadiness;
  },
});

assert.deepEqual(restoreReadinessCalls, [
  [
    "loadMachineSessionSnapshot",
    "ui-machine-session",
    "xyzab-tdr",
    { filename: "ui-machine-session.json" },
  ],
  ["loadMachineTextFiles", "xyzab-tdr"],
  ["readReadiness"],
]);
assert.equal(restoreReadinessWorkflow.readiness, readySessionReadiness);
assert.deepEqual(restoreReadinessWorkflow.fields, {
  sessionIni: "linuxcnc/machines/xyzab-tdr/machine.ini",
  sessionParameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  sessionToolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
  sessionGcode: "linuxcnc/gcode/ui-session.ngc",
  sessionSnapshot: "ui-machine-session/ui-machine-session.json",
  sessionReadinessPhase: "ready",
  sessionReadinessReady: "ready",
  sessionReadinessMissing: "none",
});
assert.deepEqual(restoreReadinessWorkflow.logLines, [
  "Snapshot: ui-machine-session/ui-machine-session.json",
  "Workflow: save-session-snapshot",
  "Default G-code: linuxcnc/gcode/ui-session.ngc",
  "INI: linuxcnc/machines/xyzab-tdr/machine.ini",
  "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var",
  "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl",
  "G-code: linuxcnc/gcode/ui-session.ngc",
  ...machineSessionReadinessLogLines(readySessionReadiness),
]);

const restoreLoadCalls = [];
const restoreLoadWorkflow = await restoreMachineSessionSnapshotWorkflow({
  machineId: "xyzab-tdr",
  snapshotId: "ui-machine-session",
  snapshotFilename: "ui-machine-session.json",
  snapshotLabel: "ui-machine-session/ui-machine-session.json",
  loadMachineSessionSnapshot: async (snapshotId, machineId, options) => {
    restoreLoadCalls.push(["loadMachineSessionSnapshot", snapshotId, machineId, options]);
    return {
      sessionId: snapshotId,
      metadata: {
        workflow: "save-session-snapshot",
        snapshotLabel: "ui-machine-session/ui-machine-session.json",
        defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
      },
      payload: {
        files: {
          ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
          parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
          toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
          gcode: "linuxcnc/gcode/ui-session.ngc",
        },
      },
    };
  },
  loadMachineTextFiles: async (machineId) => {
    restoreLoadCalls.push(["loadMachineTextFiles", machineId]);
    return {
      ini: "[EMC]\nMACHINE = xyzab-tdr\n",
    };
  },
  loadSession: async () => {
    restoreLoadCalls.push(["loadSession"]);
    return loadedSessionFixture;
  },
});

assert.deepEqual(restoreLoadCalls, [
  [
    "loadMachineSessionSnapshot",
    "ui-machine-session",
    "xyzab-tdr",
    { filename: "ui-machine-session.json" },
  ],
  ["loadMachineTextFiles", "xyzab-tdr"],
  ["loadSession"],
]);

assert.deepEqual(restoreLoadWorkflow.fields, {
  sessionIni: "/work/session-machine.ini",
  sessionParameters: "/work/session-linuxcnc.var",
  sessionToolTable: "/work/session-tool.tbl",
  sessionGcode: "linuxcnc/gcode/ui-session.ngc",
  sessionSnapshot: "ui-machine-session/ui-machine-session.json",
});
assert.deepEqual(restoreLoadWorkflow.loadSummary, loadWorkflow.loadSummary);
assert.equal(restoreLoadWorkflow.loadedSession, loadedSessionFixture);

assert.deepEqual(restoreLoadWorkflow.logLines, [
  "Snapshot: ui-machine-session/ui-machine-session.json",
  "Workflow: save-session-snapshot",
  "Default G-code: linuxcnc/gcode/ui-session.ngc",
  "Loaded machine session into the interpreter WASM filesystem.",
  "INI: linuxcnc/machines/xyzab-tdr/machine.ini -> /work/session-machine.ini",
  "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> /work/session-linuxcnc.var",
  "Parameters: restore_parameters=0",
  "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl -> /work/session-tool.tbl",
  "Tool table: tooldata_load=0",
  "G-code: linuxcnc/gcode/ui-session.ngc",
]);

console.log("ini_panel_session_workflow_node_smoke=ok");
