meta {
  name: POST craft gather refine make spine
  type: http
  seq: 5
}

docs {
  NEO-70 prototype spine: accumulate >=11 scrap_metal_bulk (gather when nodes allow, reuse persisted inventory after dotnet test), refine_scrap_efficient when refined_plate_stock < 2, then make_field_stim_mk0. No inventory POST shortcuts for spine materials.
}

script:pre-request {
  const axios = require("axios");
  const { getInventory, sumItemQuantity } = require("./scripts/inventory-api-helper.js");
  const baseUrl = bru.getEnvVar("baseUrl") || bru.getVar("baseUrl");
  const playerId = bru.getEnvVar("playerId") || bru.getVar("playerId");
  const jsonHeaders = { headers: { "Content-Type": "application/json" } };

  const progressionBefore = await axios.get(`${baseUrl}/game/players/${playerId}/skill-progression`);
  const refineBefore = progressionBefore.data.skills.find((row) => row.id === "refine");
  bru.setVar("refineXpBeforeSpine", refineBefore ? refineBefore.xp : 0);

  async function currentScrap() {
    return sumItemQuantity(await getInventory(bru), "scrap_metal_bulk");
  }

  async function currentRefined() {
    return sumItemQuantity(await getInventory(bru), "refined_plate_stock");
  }

  async function moveNear(x, z) {
    await axios.post(
      `${baseUrl}/game/players/${playerId}/move`,
      { schemaVersion: 1, target: { x, y: 0.9, z } },
      jsonHeaders,
    );
  }

  async function tryGather(interactableId) {
    const response = await axios.post(
      `${baseUrl}/game/players/${playerId}/interact`,
      { schemaVersion: 1, interactableId },
      jsonHeaders,
    );
    if (response.status !== 200) {
      throw new Error(`gather ${interactableId} HTTP ${response.status}`);
    }
    if (response.data?.allowed === true) {
      return;
    }
    if (response.data?.reasonCode === "node_depleted") {
      return;
    }
    throw new Error(`gather ${interactableId} denied: ${JSON.stringify(response.data)}`);
  }

  async function craft(recipeId) {
    const response = await axios.post(
      `${baseUrl}/game/players/${playerId}/craft`,
      { schemaVersion: 1, recipeId },
      jsonHeaders,
    );
    if (response.status !== 200 || response.data?.success !== true) {
      throw new Error(`craft ${recipeId} failed: ${response.status} ${JSON.stringify(response.data)}`);
    }
    return response.data;
  }

  const gatherPlan = [
    { id: "prototype_urban_bulk_delta", x: 16, z: -2 },
    { id: "prototype_bio_mat_gamma", x: 10, z: -2 },
    { id: "prototype_subsurface_vein_beta", x: 16, z: -6 },
    { id: "prototype_resource_node_alpha", x: 10, z: -6 },
  ];

  let scrap = await currentScrap();
  for (const node of gatherPlan) {
    if (scrap >= 11) {
      break;
    }
    await moveNear(node.x, node.z);
    await tryGather(node.id);
    scrap = await currentScrap();
  }

  if (scrap < 11) {
    throw new Error(`spine needs >=11 scrap_metal_bulk, have ${scrap} after optional gathers`);
  }

  if ((await currentRefined()) < 2) {
    await craft("refine_scrap_efficient");
  }
}

post {
  url: {{baseUrl}}/game/players/{{playerId}}/craft
  body: json
  auth: none
}

headers {
  content-type: application/json
}

body:json {
  {
    "schemaVersion": 1,
    "recipeId": "make_field_stim_mk0"
  }
}

script:post-response {
  const axios = require("axios");
  const baseUrl = bru.getEnvVar("baseUrl") || bru.getVar("baseUrl");
  const playerId = bru.getEnvVar("playerId") || bru.getVar("playerId");

  const inventory = await axios.get(`${baseUrl}/game/players/${playerId}/inventory`);
  const progression = await axios.get(`${baseUrl}/game/players/${playerId}/skill-progression`);

  bru.setVar("spineInventory", inventory.data);
  bru.setVar("spineProgression", progression.data);
}

tests {
  test("make_field_stim_mk0 succeeds after gather and refine", function () {
    expect(res.getStatus()).to.equal(200);
    const body = res.getBody();
    expect(body.schemaVersion).to.equal(1);
    expect(body.success).to.equal(true);
    expect(body.reasonCode).to.equal(null);
    expect(body.outputsGranted).to.deep.include({ itemId: "field_stim_mk0", quantity: 1 });
    expect(body.xpGrantSummary).to.be.an("object");
    expect(body.xpGrantSummary.skillId).to.equal("refine");
    expect(body.xpGrantSummary.amount).to.equal(10);
    expect(body.xpGrantSummary.sourceKind).to.equal("activity");
  });

  test("inventory includes field_stim_mk0 after spine", function () {
    const inventory = bru.getVar("spineInventory");
    const total = inventory.bagSlots
      .filter((slot) => slot.itemId === "field_stim_mk0")
      .reduce((sum, slot) => sum + slot.quantity, 0);
    expect(total).to.be.at.least(1);
  });

  test("refine skill XP increased after spine crafts", function () {
    const progression = bru.getVar("spineProgression");
    const refine = progression.skills.find((row) => row.id === "refine");
    const before = bru.getVar("refineXpBeforeSpine");
    expect(refine).to.be.an("object");
    expect(refine.xp - before).to.be.at.least(10);
  });
}
