import {
  createRunSummary,
} from "./run-summary.js";
import {
  createSessionSnapshotSummary,
  sessionSnapshotMetadataLogLines,
} from "./session-summary.js";

export function selectSessionGcodeProgram({
  snapshot = null,
  defaultFilename,
  defaultOpfsPath,
  wasmPath,
  gcodeFilenameFromProgramPath,
}) {
  const snapshotOpfsPath = snapshot?.payload?.files?.gcode;
  if (typeof snapshotOpfsPath === "string") {
    try {
      return {
        filename: gcodeFilenameFromProgramPath(snapshotOpfsPath),
        opfsPath: snapshotOpfsPath,
        source: "snapshot",
        wasmPath,
      };
    } catch {
      // Invalid snapshot program paths fall back to the UI default program.
    }
  }
  return {
    filename: defaultFilename,
    opfsPath: defaultOpfsPath,
    source: "default",
    wasmPath,
  };
}

export function createRunSessionReadinessState({
  loadedSession = null,
  snapshot = null,
  defaultFilename,
  defaultOpfsPath,
  wasmPath,
  gcodeFilenameFromProgramPath,
}) {
  const program = selectSessionGcodeProgram({
    snapshot,
    defaultFilename,
    defaultOpfsPath,
    wasmPath,
    gcodeFilenameFromProgramPath,
  });
  const missing = [];
  if (!loadedSession?.ini?.wasmPath) {
    missing.push("loaded-session");
  }
  if (!program.filename || !program.opfsPath || !program.wasmPath) {
    missing.push("gcode-program");
  }
  const canRun = missing.length === 0;
  return {
    phase: canRun ? "ready" : "blocked",
    canRun,
    missing,
    loadedSession: {
      iniWasmPath: loadedSession?.ini?.wasmPath ?? null,
    },
    snapshot: {
      present: Boolean(snapshot),
      label: snapshot?.metadata?.snapshotLabel ?? snapshot?.sessionId ?? null,
      workflow: snapshot?.metadata?.workflow ?? null,
      gcodeOpfsPath: snapshot?.payload?.files?.gcode ?? null,
    },
    defaultProgram: {
      filename: defaultFilename ?? null,
      opfsPath: defaultOpfsPath ?? null,
      wasmPath: wasmPath ?? null,
    },
    program,
    runButton: {
      enabled: canRun,
      reason: canRun ? null : `Missing ${missing.join(", ")}.`,
    },
  };
}

export function runSessionContextLogLines(program, snapshot = null, snapshotLabel = null) {
  const summary = createSessionSnapshotSummary({
    metadata: snapshot?.metadata ?? {},
  });
  return [
    `Program: ${program.opfsPath} -> ${program.wasmPath}`,
    `Program source: ${program.source}`,
    `Snapshot: ${snapshot ? (snapshotLabel ?? snapshot?.metadata?.snapshotLabel ?? snapshot?.sessionId) : "-"}`,
    ...sessionSnapshotMetadataLogLines(summary),
  ];
}

export async function runGcodeSessionWorkflow({
  interp,
  loadedSession,
  snapshot = null,
  defaultFilename,
  defaultOpfsPath,
  wasmPath,
  gcodeFilenameFromProgramPath,
  loadGcodeProgram,
  summarizeRun = () => ({}),
  snapshotLabel = null,
}) {
  const readiness = createRunSessionReadinessState({
    loadedSession,
    snapshot,
    defaultFilename,
    defaultOpfsPath,
    wasmPath,
    gcodeFilenameFromProgramPath,
  });
  if (!readiness.canRun) {
    throw new Error("Load a machine session before running G-code.");
  }
  const program = readiness.program;
  const programText = await loadGcodeProgram(program.filename);
  interp.writeTextFile(program.wasmPath, programText);
  const resultText = interp.runFileWithIni(program.wasmPath, loadedSession.ini.wasmPath).trim();
  const runResultSummary = summarizeRun(resultText, programText);
  const { motionSnapshots = [], ...runCounts } = runResultSummary;
  const summary = createRunSummary(
    program,
    {
      runStatus: "ok",
      iniWasmPath: loadedSession.ini.wasmPath,
      snapshotLabel: snapshot ? snapshotLabel : null,
      ...runCounts,
    },
    snapshot,
  );
  return {
    program: {
      ...program,
      text: programText,
    },
    resultText,
    motionSnapshots,
    summary,
    readiness,
    fields: {
      sessionGcode: program.opfsPath,
      runStatus: summary.runStatus,
      runSource: summary.programSource,
      runSnapshot: summary.snapshotLabel ?? "-",
      runWorkflow: summary.workflow ?? "-",
      runOpfsPath: summary.programOpfsPath,
      runWasmPath: summary.programWasmPath,
    },
    logLines: [
      ...runSessionContextLogLines(program, snapshot, summary.snapshotLabel),
      `INI: ${loadedSession.ini.wasmPath}`,
      resultText,
    ],
  };
}
