import type { RequestBudgetSnapshot } from "@best-agent/protocol";
import { describe, expect, it, vi } from "vitest";
import { runProviderDecisionTurn, type ProviderDecisionPreview } from "./provider-turn-runner.js";

describe("runProviderDecisionTurn", () => {
  it("rebuilds the decide request once after context overflow", async () => {
    const contextOverflow = new Error("too much context");
    const previews: ProviderDecisionPreview[] = [];
    const invokeDecide = vi
      .fn()
      .mockRejectedValueOnce(contextOverflow)
      .mockResolvedValueOnce({
        kind: "respond",
        model: "test-model",
        provider: "openai",
        text: "done",
      });

    const result = await runProviderDecisionTurn({
      assertTurnAttemptWritable: vi.fn(async () => undefined),
      availableTools: [],
      buildDecideMemoryRequest: vi.fn(async (options?: { forceCompaction?: boolean }) => ({
        request: {
          messages: [
            {
              role: "user" as const,
              content: options?.forceCompaction ? "compacted" : "initial",
            },
          ],
          systemPrompt: options?.forceCompaction ? "compacted prompt" : "initial prompt",
          tools: [],
        },
        snapshot: buildSnapshot(options?.forceCompaction ? "compacted" : "budget-ok"),
      })),
      canRetryContextOverflow: (error) => error === contextOverflow,
      controls: {
        allowPhaseResult: false,
        allowPlanUpdate: false,
      },
      createDecisionPreview: () => createPreview(previews),
      invokeDecide,
      noActionTimeoutMs: () => 1,
      providerTurnBudgeted: true,
      resolveDecision: vi.fn(async (input) =>
        input.decision.kind === "respond" ? input.decision.text : "",
      ),
      resolveRequiredToolConstraint: () => undefined,
      resolveVisibleTools: () => [],
    });

    expect(result.responseText).toBe("done");
    expect(invokeDecide).toHaveBeenCalledTimes(2);
    expect(previews.every((preview) => vi.mocked(preview.finalize).mock.calls.length === 1)).toBe(
      true,
    );
  });
});

function buildSnapshot(autoBudgetOutcome: RequestBudgetSnapshot["autoBudgetOutcome"]) {
  return {
    autoBudgetOutcome,
    estimatedInputTokens: 1,
    updatedAt: "2026-05-20T00:00:00.000Z",
    updatedAtMs: Date.parse("2026-05-20T00:00:00.000Z"),
    usedAutoCompaction: autoBudgetOutcome === "compacted",
  } satisfies RequestBudgetSnapshot;
}

function createPreview(previews: ProviderDecisionPreview[]): ProviderDecisionPreview {
  const preview: ProviderDecisionPreview = {
    callbacks: {},
    finalize: vi.fn(async () => undefined),
    resolveToolCallId: vi.fn(() => undefined),
  };
  previews.push(preview);
  return preview;
}
