/*
 * The run-result envelope: what parseRunResult will and won't accept.
 *
 * Everything this parses arrived as text a language model chose to paste into
 * its reply, so the interesting cases are all the malformed ones. A bad envelope
 * has to return null - the caller then shows the raw block as code, which is
 * ugly but honest - rather than yield a half-built object that renders as a run
 * that never happened.
 */
import { test } from "node:test";
import assert from "node:assert/strict";

import { RUN_LANGS, RUN_FENCE_LANG, parseRunResult } from "./run-langs.js";

const envelope = (over = {}) => JSON.stringify({
  lang: "python",
  source: "print(1)",
  stdout: "1\n",
  stderr: "",
  exit_code: 0,
  ...over,
});

test("the fence tag is the one the backend emits", () => {
  assert.equal(RUN_FENCE_LANG, "nexus-run");
});

test("every language has a display label", () => {
  for (const [name, spec] of Object.entries(RUN_LANGS)) {
    assert.equal(typeof spec.label, "string", name);
    assert.ok(spec.label.length, name);
  }
});

test("a well-formed envelope parses into display fields", () => {
  const run = parseRunResult(envelope());
  assert.equal(run.lang, "python");
  assert.equal(run.label, "Python");
  assert.equal(run.source, "print(1)");
  assert.equal(run.stdout, "1\n");
  assert.equal(run.exitCode, 0);
});

test("a backtick-escaped source round-trips through JSON", () => {
  // run_snippet re-encodes ` as ` so the source cannot close the fence.
  const run = parseRunResult('{"lang":"python","source":"x = \\u0060a\\u0060","exit_code":0}');
  assert.equal(run.source, "x = `a`");
});

test("a non-zero exit code is preserved, not coerced away", () => {
  // `exitCode || null` would turn 0 into null and hide a clean exit; a plain
  // falsy check on the other side would call a failing program successful.
  assert.equal(parseRunResult(envelope({ exit_code: 2 })).exitCode, 2);
  assert.equal(parseRunResult(envelope({ exit_code: 0 })).exitCode, 0);
});

test("a missing exit code becomes null rather than a guess", () => {
  assert.equal(parseRunResult(envelope({ exit_code: undefined })).exitCode, null);
  assert.equal(parseRunResult(envelope({ exit_code: "0" })).exitCode, null);
});

test("absent streams read as empty, never undefined", () => {
  const run = parseRunResult('{"lang":"c","source":"int main(){}","exit_code":0}');
  assert.equal(run.stdout, "");
  assert.equal(run.stderr, "");
});

test("malformed or foreign envelopes are rejected", () => {
  assert.equal(parseRunResult("not json at all"), null);
  assert.equal(parseRunResult("null"), null);
  assert.equal(parseRunResult("[1,2,3]"), null);
  assert.equal(parseRunResult('"a string"'), null);
  assert.equal(parseRunResult(envelope({ lang: "haskell" })), null);
  assert.equal(parseRunResult(envelope({ source: undefined })), null);
  assert.equal(parseRunResult(envelope({ source: 42 })), null);
});

test("a prototype key is not mistaken for a supported language", () => {
  // `data.lang in RUN_LANGS` would be true for "toString" and read the label
  // off Object.prototype - a run panel titled with a function body.
  assert.equal(parseRunResult(envelope({ lang: "toString" })), null);
  assert.equal(parseRunResult(envelope({ lang: "constructor" })), null);
});
