import {
  createMachineSessionLoadSummary,
  machineSessionLoadLogLines,
} from "./session-load-summary.js";
import {
  createSessionSnapshotSummary,
  sessionSnapshotSummaryLogLines,
} from "./session-summary.js";

export function createSessionSnapshotFieldState(summary) {
  return {
    sessionIni: summary.iniOpfsPath,
    sessionParameters: summary.parameterOpfsPath,
    sessionToolTable: summary.toolTableOpfsPath,
    sessionGcode: summary.gcodeOpfsPath,
    sessionSnapshot: summary.snapshotLabel,
  };
}

export function sessionSnapshotSaveLogLines(summary) {
  return [
    ...sessionSnapshotSummaryLogLines(summary),
    `INI: ${summary.iniOpfsPath}`,
    `Parameter file: ${summary.parameterOpfsPath}`,
    `Tool table file: ${summary.toolTableOpfsPath}`,
    `G-code: ${summary.gcodeOpfsPath}`,
  ];
}

export function sessionSnapshotRestoreLogLines(summary) {
  return [
    ...sessionSnapshotSummaryLogLines(summary),
    `INI: ${summary.iniOpfsPath}`,
    `Parameter file: ${summary.parameterOpfsPath}`,
    `Tool table file: ${summary.toolTableOpfsPath}`,
    `G-code: ${summary.gcodeOpfsPath ?? "-"}`,
  ];
}

export function createMachineSessionLoadFieldState(summary) {
  return {
    sessionIni: summary.iniWasmPath,
    sessionParameters: summary.parameterWasmPath,
    sessionToolTable: summary.toolTableWasmPath,
  };
}

export function machineSessionLoadWorkflowLogLines(summary) {
  return [
    "Loaded machine session into the interpreter WASM filesystem.",
    ...machineSessionLoadLogLines(summary),
  ];
}

export function createMachineSessionReadinessFieldState(readiness) {
  return {
    sessionReadinessPhase: readiness.phase,
    sessionReadinessReady: readiness.ready ? "ready" : "blocked",
    sessionReadinessMissing: readiness.missing.length === 0 ? "none" : readiness.missing.join(", "),
  };
}

export function machineSessionReadinessLogLines(readiness) {
  const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
  return [
    `Session readiness: ${readiness.ready ? "ready" : "blocked"}`,
    ...checks.map((check) => {
      const status = check.ok ? "ok" : `blocked (${check.error})`;
      return `${check.name}: ${check.path} -> ${status}`;
    }),
    ...(readiness.snapshotCheck
      ? [
          `snapshot: ${readiness.snapshotCheck.path} -> ${
            readiness.snapshotCheck.ok ? "ok" : `blocked (${readiness.snapshotCheck.error})`
          }`,
        ]
      : []),
  ];
}

export async function readMachineSessionReadinessWorkflow({
  readiness,
  readReadiness,
} = {}) {
  const sessionReadiness = readiness ?? await readReadiness?.();
  if (!sessionReadiness) {
    throw new Error("A machine session readiness reader is required.");
  }
  return {
    readiness: sessionReadiness,
    fields: createMachineSessionReadinessFieldState(sessionReadiness),
    logLines: machineSessionReadinessLogLines(sessionReadiness),
  };
}

export async function loadMachineSessionIntoWasmWorkflow({ loadSession }) {
  if (typeof loadSession !== "function") {
    throw new Error("A machine session loader is required.");
  }
  const loadedSession = await loadSession();
  const loadSummary = createMachineSessionLoadSummary(loadedSession);
  return {
    loadedSession,
    loadSummary,
    fields: createMachineSessionLoadFieldState(loadSummary),
    logLines: machineSessionLoadWorkflowLogLines(loadSummary),
  };
}

export async function saveMachineSessionSnapshotWorkflow({
  machineId,
  snapshotId,
  snapshotFilename,
  snapshotLabel,
  iniText,
  toolTableText,
  parameterText,
  gcodeFilename,
  gcodeText,
  metadata,
  saveMachineTextFiles,
  saveGcodeProgram,
  saveMachineSessionSnapshot,
}) {
  await Promise.all([
    saveMachineTextFiles(machineId, {
      ini: iniText,
      toolTable: toolTableText,
      parameters: parameterText,
    }),
    saveGcodeProgram(gcodeFilename, gcodeText),
  ]);
  const snapshot = await saveMachineSessionSnapshot(
    snapshotId,
    machineId,
    {
      filename: snapshotFilename,
      gcodeFilename,
      metadata,
    },
  );
  const summary = createSessionSnapshotSummary(snapshot, snapshotLabel);
  return {
    snapshot,
    summary,
    fields: createSessionSnapshotFieldState(summary),
    logLines: sessionSnapshotSaveLogLines(summary),
  };
}

export async function restoreMachineSessionSnapshotWorkflow({
  machineId,
  snapshotId,
  snapshotFilename,
  snapshotLabel,
  loadMachineSessionSnapshot,
  loadMachineTextFiles,
  readReadiness = null,
  loadSession = null,
}) {
  const snapshot = await loadMachineSessionSnapshot(
    snapshotId,
    machineId,
    { filename: snapshotFilename },
  );
  const files = await loadMachineTextFiles(machineId);
  const summary = createSessionSnapshotSummary(snapshot, snapshotLabel ?? snapshotLabelFromSession(snapshot));
  const fields = createSessionSnapshotFieldState(summary);
  const result = {
    snapshot,
    editorIniText: files.ini,
    summary,
    fields,
    readiness: null,
    loadSummary: null,
    loadedSession: null,
    logLines: sessionSnapshotRestoreLogLines(summary),
  };
  if (readReadiness) {
    const readinessWorkflow = await readMachineSessionReadinessWorkflow({ readReadiness });
    result.readiness = readinessWorkflow.readiness;
    result.fields = {
      ...result.fields,
      ...readinessWorkflow.fields,
    };
    result.logLines = [
      ...result.logLines,
      ...readinessWorkflow.logLines,
    ];
  }
  if (loadSession) {
    const loadWorkflow = await loadMachineSessionIntoWasmWorkflow({ loadSession });
    result.loadedSession = loadWorkflow.loadedSession;
    result.loadSummary = loadWorkflow.loadSummary;
    result.fields = {
      ...fields,
      ...(result.readiness ? createMachineSessionReadinessFieldState(result.readiness) : {}),
      ...loadWorkflow.fields,
      sessionGcode: summary.gcodeOpfsPath,
    };
    result.logLines = [
      ...sessionSnapshotSummaryLogLines(summary),
      ...(result.readiness ? machineSessionReadinessLogLines(result.readiness) : []),
      ...loadWorkflow.logLines,
      `G-code: ${summary.gcodeOpfsPath ?? "-"}`,
    ];
  }
  return result;
}

function snapshotLabelFromSession(snapshot) {
  return snapshot?.metadata?.snapshotLabel ?? snapshot?.sessionId ?? null;
}
