import { Effect } from "effect";
import { describe, expect, it } from "vitest";

import {
  decodeIssueWorkspaceResult,
  makeIssueWorkspacePlan,
  transitionWorkspaceRun,
  WorkspaceExecutionError,
  WorkspaceStateError,
  WorkspaceValidationError,
} from "./project-workspace";

const makeInput = () => ({
  artifacts: [
    {
      content: "# Work\n",
      path: "work.md",
    },
  ],
  branchName: undefined,
  checkoutPath: undefined,
  contextFiles: [
    {
      content: "# Project\n",
      kind: "readme" as const,
      path: "README.md",
    },
  ],
  defaultBranch: "main",
  issueBody: "Make the project run in an isolated checkout.",
  issueId: "projectIssues|abc123",
  issueNumber: 7,
  issueTitle: "Use a real project checkout",
  sourceUrl: "https://git.example.test/puter/project",
});

const validationFailure = (input: unknown): WorkspaceValidationError => {
  const error = Effect.runSync(Effect.flip(makeIssueWorkspacePlan(input)));
  expect(error).toBeInstanceOf(WorkspaceValidationError);
  return error;
};

describe("project workspace primitives", () => {
  it("plans a deterministic checkout and keeps control files outside the repo", () => {
    const plan = Effect.runSync(makeIssueWorkspacePlan(makeInput()));
    const secondPlan = Effect.runSync(makeIssueWorkspacePlan(makeInput()));

    expect(plan.branchName).toBe("work/issue-7-abc123");
    expect(plan.checkoutPath).toBe("/workspace/repository");
    expect(plan).toEqual(secondPlan);
    expect(plan.operations).toEqual(
      expect.arrayContaining([
        {
          _tag: "WriteFile",
          content: expect.any(String),
          path: "/workspace/control/issue.md",
        },
        {
          _tag: "WriteFile",
          content: "# Project\n",
          path: "/workspace/control/context/README.md",
        },
        {
          _tag: "WriteFile",
          content: "# Work\n",
          path: "/workspace/control/artifacts/work.md",
        },
      ])
    );
    expect(
      plan.operations.some(
        (operation) =>
          operation._tag === "Exec" && operation.command.includes("git clone")
      )
    ).toBe(true);
  });

  it("rejects unsafe sources and control-file paths", () => {
    const missingSource = { ...makeInput(), sourceUrl: undefined };
    expect(validationFailure(missingSource).reason).toBe("MissingSource");

    const invalidSource = makeInput();
    invalidSource.sourceUrl = "ssh://git@example.test/project";
    expect(validationFailure(invalidSource).reason).toBe("InvalidSourceUrl");

    const invalidPath = makeInput();
    const [artifact] = invalidPath.artifacts;
    if (!artifact) {
      throw new Error("test artifact missing");
    }
    artifact.path = "../work.md";
    expect(validationFailure(invalidPath).reason).toBe("InvalidPath");
  });

  it("rejects a checkout result that does not prove the planned branch", () => {
    const plan = Effect.runSync(makeIssueWorkspacePlan(makeInput()));
    const error = Effect.runSync(
      Effect.flip(
        decodeIssueWorkspaceResult({
          command: {
            exitCode: 0,
            stderr: "",
            stdout: "work/issue-7-other deadbeef\n",
          },
          plan,
        })
      )
    );

    expect(error).toBeInstanceOf(WorkspaceExecutionError);
    expect(error.reason).toBe("InvalidResult");
  });

  it("accepts the planned checkout result and enforces run transitions", () => {
    const plan = Effect.runSync(makeIssueWorkspacePlan(makeInput()));
    const result = Effect.runSync(
      decodeIssueWorkspaceResult({
        command: {
          exitCode: 0,
          stderr: "",
          stdout: `${plan.branchName} deadbeef1234567\n`,
        },
        plan,
      })
    );

    expect(result).toMatchObject({
      branchName: plan.branchName,
      checkoutPath: "/workspace/repository",
      headSha: "deadbeef1234567",
    });
    expect(
      Effect.runSync(transitionWorkspaceRun({ from: "queued", to: "working" }))
    ).toBe("working");

    const error = Effect.runSync(
      Effect.flip(transitionWorkspaceRun({ from: "completed", to: "working" }))
    );
    expect(error).toBeInstanceOf(WorkspaceStateError);
  });
});
