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

import { FakeHarnessLive } from "./harness-runtime";
import { defaultCodingKitV0, resolveOutcome, shouldRetry } from "./resolver";
import { decodeDefinition, validateDefinition } from "./work-definition";
import { validateDesignPacket } from "./work-design";
import { canTransitionWork } from "./work-lifecycle";

const definition = {
  acceptanceCriteria: ["terminal outcome"],
  affectedUsers: ["founders"],
  assumptions: [],
  constraints: [],
  desiredOutcome: "A visible terminal simulation exists",
  inScope: ["fake execution"],
  outOfScope: ["real sandboxing"],
  problem: "A deterministic build path is needed",
  questions: [
    {
      alternatives: [],
      id: "q1",
      impact: "high",
      prompt: "Which runtime?",
      status: "open",
    },
  ],
  requiredArtifacts: ["events"],
  risk: "medium",
  version: 1,
};

const probeOutcome = (
  classification: Parameters<typeof resolveOutcome>[0]["classification"],
  retryable = false
) => ({ classification, retryable, summary: "probe" });

describe("work resolution contracts", () => {
  test("high-impact open questions block definition approval", async () => {
    await expect(
      Effect.runPromise(decodeDefinition(definition))
    ).resolves.toEqual(definition);
    await expect(
      Effect.runPromise(validateDefinition(definition))
    ).rejects.toThrow(/High-impact/u);
  });

  test("question identities are unique within a Definition version", async () => {
    await expect(
      Effect.runPromise(
        decodeDefinition({
          ...definition,
          questions: [definition.questions[0], definition.questions[0]],
        })
      )
    ).rejects.toThrow(/Question ID must be unique/u);
  });

  test("observable slices and terminal fake outcomes are enforced", async () => {
    const approved = await Effect.runPromise(
      validateDefinition({ ...definition, questions: [] })
    );
    const design = await Effect.runPromise(
      validateDesignPacket({
        architectureSummary: "small",
        callFlowDelta: [],
        concerns: [],
        definitionVersion: approved.version,
        evidenceRequirements: ["event"],
        fileTreeDelta: [],
        impactMap: { files: [], modules: [], risks: [], summary: "small" },
        invariants: ["terminal"],
        keyInterfaces: [],
        slices: [
          {
            codeBoundaries: ["runtime"],
            dependsOn: [],
            evidenceRequirements: ["event"],
            id: "s1",
            objective: "one",
            observableBehavior: "visible",
            reviewRequired: false,
            title: "one",
            verification: ["assert"],
          },
        ],
        tradeoffs: [],
        version: 1,
      })
    );
    expect(design.slices).toHaveLength(1);
    const [, outcome] = await Effect.runPromise(
      FakeHarnessLive(() => 1).run({ scenario: "success" })
    );
    expect(outcome.classification).toBe("Succeeded");
    expect(outcome.summary).toMatch(/no implementation/u);
    const [, firstTransient] = await Effect.runPromise(
      FakeHarnessLive(() => 1).run({
        attemptNumber: 1,
        scenario: "transient-failure-then-success",
      })
    );
    const [, recoveredTransient] = await Effect.runPromise(
      FakeHarnessLive(() => 1).run({
        attemptNumber: 2,
        scenario: "transient-failure-then-success",
      })
    );
    expect(firstTransient.classification).toBe("RetryableFailure");
    expect(recoveredTransient.classification).toBe("Succeeded");
    expect(
      shouldRetry(
        {
          classification: "RetryableFailure",
          retryable: true,
          summary: "retry",
        },
        1,
        defaultCodingKitV0.retryPolicy
      )
    ).toBe(true);
    expect(canTransitionWork("ready", "executing")).toBe(true);
  });
});

describe("resolver decisions", () => {
  const policy = defaultCodingKitV0.retryPolicy;

  test("retries a retryable failure while the budget remains", () => {
    expect(
      resolveOutcome(probeOutcome("RetryableFailure", true), 1, policy)
    ).toEqual({ kind: "retry" });
  });

  test("settles RetryableFailure as failed once the budget is exhausted", () => {
    expect(
      resolveOutcome(probeOutcome("RetryableFailure", true), 3, policy)
    ).toEqual({
      kind: "terminal",
      workStatus: "failed",
    });
  });

  test("maps each terminal classification to its Work status", () => {
    expect(resolveOutcome(probeOutcome("Succeeded"), 1, policy)).toEqual({
      kind: "terminal",
      workStatus: "completed",
    });
    expect(resolveOutcome(probeOutcome("PermanentFailure"), 1, policy)).toEqual(
      {
        kind: "terminal",
        workStatus: "failed",
      }
    );
    expect(resolveOutcome(probeOutcome("NeedsInput"), 1, policy)).toEqual({
      kind: "terminal",
      workStatus: "needs-input",
    });
    expect(resolveOutcome(probeOutcome("Blocked"), 1, policy)).toEqual({
      kind: "terminal",
      workStatus: "blocked",
    });
    expect(resolveOutcome(probeOutcome("Cancelled"), 1, policy)).toEqual({
      kind: "terminal",
      workStatus: "ready",
    });
  });

  test("completed and cancelled are not silently reopened", () => {
    expect(canTransitionWork("completed", "defining")).toBe(false);
    expect(canTransitionWork("cancelled", "proposed")).toBe(false);
    expect(canTransitionWork("ready", "executing")).toBe(true);
  });
});
