import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import type {
  ArtifactObligation,
  ThreadEvidenceRecord,
  ThreadId,
  TurnId,
} from "@best-agent/protocol";
import { describe, expect, it } from "vitest";
import {
  collectArtifactObligationFailures,
  compileArtifactObligations,
} from "./artifact-obligations.js";

describe("artifact obligations", () => {
  it("validates command-zero-exit from shell evidence instead of executing commands", () => {
    const cwd = process.cwd();
    const obligation: ArtifactObligation = {
      kind: "run-command",
      obligationId: "run-tests",
      required: true,
      target: "pnpm test",
      verifier: "command-zero-exit",
    };
    const evidence: ThreadEvidenceRecord = {
      confidence: 0.8,
      createdAt: "2026-05-13T00:00:00.000Z",
      evidenceId: "evidence-1",
      freshness: "2026-05-13T00:00:00.000Z",
      kind: "tool-outcome",
      payload: {
        command: "pnpm test",
        evidencePayload: {
          exitCode: 0,
          signalCode: null,
          timedOut: false,
        },
        outcome: "success",
      },
      sourceToolName: "shell",
      summary: "Shell command success: pnpm test.",
      threadId: "thread-1" as ThreadId,
      turnId: "turn-1" as TurnId,
    };

    expect(
      collectArtifactObligationFailures([obligation], cwd, { evidenceRecords: [evidence] }),
    ).toEqual([]);
    expect(collectArtifactObligationFailures([obligation], cwd)).toEqual([
      expect.objectContaining({ obligationId: "run-tests", reason: "evidence-missing" }),
    ]);
  });

  it("does not create artifact obligations for review or explanation prompts", () => {
    expect(
      compileArtifactObligations({
        artifactProducing: false,
        cwd: process.cwd(),
        objective: "review packages/engine/src/provider/provider-core.ts and explain how to fix it",
      }),
    ).toEqual([]);
  });

  it("creates file obligations for analysis prompts with explicit output targets", () => {
    const cwd = process.cwd();

    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "review the design and save the summary to docs/review.md",
      }),
    ).toEqual([
      expect.objectContaining({
        target: resolve(cwd, "docs/review.md"),
      }),
    ]);
  });

  it("creates file obligations for explicit output prompts", () => {
    const cwd = process.cwd();

    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "write the final answer to docs/report.md",
      }),
    ).toEqual([
      expect.objectContaining({
        kind: "write-target",
        target: resolve(cwd, "docs/report.md"),
        verifier: "exists",
      }),
    ]);
  });

  it("does not create output obligations without artifact-producing tools", () => {
    expect(
      compileArtifactObligations({
        artifactProducing: false,
        cwd: process.cwd(),
        objective: "write the final answer to docs/report.md",
      }),
    ).toEqual([]);
  });

  it("does not treat reviewed source paths as output targets", () => {
    const cwd = process.cwd();

    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "generate a review for packages/engine/src/provider/provider-core.ts",
      }),
    ).toEqual([]);
    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "write a report about packages/engine/src/provider/provider-core.ts",
      }),
    ).toEqual([]);
    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "write an explanation of src/foo.ts",
      }),
    ).toEqual([]);
    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective:
          "write a report about what changed in packages/engine/src/provider/provider-core.ts",
      }),
    ).toEqual([]);
    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "write the final report in docs/report.md",
      }),
    ).toEqual([
      expect.objectContaining({
        target: resolve(cwd, "docs/report.md"),
      }),
    ]);
  });

  it("does not infer mutation obligations from update keywords alone", () => {
    const cwd = process.cwd();
    const objectives = [
      "fix bug in src/a.ts",
      "review src/a.ts",
      "update docs/report.md",
      "modify src/a.ts",
    ];

    for (const objective of objectives) {
      const input = { cwd, objective };
      expect(compileArtifactObligations(input)).toEqual([]);
      expect(compileArtifactObligations({ ...input, artifactProducing: true })).toEqual([]);
    }
  });

  it("uses explicit deliverable path evidence for new and modified targets", () => {
    const cwd = mkdtempSync(join(tmpdir(), "best-agent-artifact-obligations-"));
    mkdirSync(join(cwd, "src"), { recursive: true });
    writeFileSync(join(cwd, "src/a.ts"), "export const value = 1;\n", "utf8");

    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "save the report to output.md",
      }),
    ).toEqual([
      expect.objectContaining({
        kind: "write-target",
        target: resolve(cwd, "output.md"),
        verifier: "exists",
      }),
    ]);

    expect(
      compileArtifactObligations({
        artifactProducing: true,
        cwd,
        objective: "modify src/a.ts and save the final result to src/a.ts",
      }),
    ).toEqual([
      expect.objectContaining({
        kind: "modify-target",
        target: resolve(cwd, "src/a.ts"),
        verifier: "changed",
      }),
    ]);
  });
});
