import { expect } from "chai";
import { runTransformCorrectionsJob } from "../../src/jobs/transform-corrections.js";

/** Same stubbing approach as `audit-low-confidence.test.ts` — see that file's own doc comment. */
function stubFetch(responses: Record<string, unknown>): { url: string; body: unknown }[] {
  const calls: { url: string; body: unknown }[] = [];
  globalThis.fetch = (async (url: string | URL, init?: RequestInit) => {
    const href = String(url);
    const body = typeof init?.body === "string" ? JSON.parse(init.body) : undefined;
    calls.push({ url: href, body });
    for (const [pathFragment, response] of Object.entries(responses)) {
      if (href.includes(pathFragment)) {
        return new Response(JSON.stringify(response), { status: 200 });
      }
    }
    throw new Error(`stubFetch: no response configured for ${href}`);
  }) as typeof fetch;
  return calls;
}

describe("runTransformCorrectionsJob", () => {
  const originalFetch = globalThis.fetch;

  afterEach(() => {
    globalThis.fetch = originalFetch;
  });

  it("submits a suggestion for each correction the LLM produces usable output for", async () => {
    const calls = stubFetch({
      "pending-corrections": [
        {
          id: 1,
          stepId: 1,
          recipeId: 1,
          clauseText: "faites-les revenir",
          start: 0,
          end: 19,
          previousTechStepKey: null,
          correctedTechStepKey: "brown",
        },
      ],
      "training-suggestions": { created: 1 },
    });
    const llm = {
      suggestTrainingData: async () => ({
        suggestedSynonyms: ["revenir"],
        suggestedUtterances: ["faites-les revenir cinq minutes"],
      }),
    };

    const count = await runTransformCorrectionsJob(llm, { limit: 10 });

    expect(count).to.equal(1);
    const postCall = calls.find((call) => call.url.includes("training-suggestions"));
    if (!postCall) throw new Error("expected a POST to training-suggestions");
    expect(postCall.body).to.deep.equal({
      suggestions: [
        {
          techStepKey: "brown",
          locale: "fr",
          suggestedSynonyms: ["revenir"],
          suggestedUtterances: ["faites-les revenir cinq minutes"],
          sourceType: "correction",
          sourceCorrectionId: 1,
        },
      ],
    });
  });

  it("posts nothing when the LLM's suggestion is empty on both arrays", async () => {
    const calls = stubFetch({
      "pending-corrections": [
        {
          id: 1,
          stepId: 1,
          recipeId: 1,
          clauseText: "x",
          start: 0,
          end: 1,
          previousTechStepKey: null,
          correctedTechStepKey: "simmer",
        },
      ],
    });
    const llm = {
      suggestTrainingData: async () => ({ suggestedSynonyms: [], suggestedUtterances: [] }),
    };

    const count = await runTransformCorrectionsJob(llm, { limit: 10 });

    expect(count).to.equal(0);
    expect(calls.some((call) => call.url.includes("training-suggestions"))).to.equal(false);
  });

  it("skips a correction with no corrected technique (defensive backstop — the API itself never returns one)", async () => {
    stubFetch({
      "pending-corrections": [
        {
          id: 1,
          stepId: 1,
          recipeId: 1,
          clauseText: "x",
          start: 0,
          end: 1,
          previousTechStepKey: "cook",
          correctedTechStepKey: null,
        },
      ],
    });
    let called = false;
    const llm = {
      suggestTrainingData: async () => {
        called = true;
        return { suggestedSynonyms: [], suggestedUtterances: [] };
      },
    };

    const count = await runTransformCorrectionsJob(llm, { limit: 10 });

    expect(count).to.equal(0);
    expect(called).to.equal(false);
  });
});
