import { describe, it, expect, vi, type Mock } from "vitest";
import { handleRewindFiles, type RewindFilesResult } from "./ws-bridge-controls.js";
import type { Session } from "./ws-bridge-types.js";

type SendControlRequestFn = (
  request: Record<string, unknown>,
  onResponse?: { subtype: string; resolve: (response: unknown) => void },
) => void;

function makeSession(): Session {
  return {
    id: "test-session",
    backendType: "claude",
    cliSocket: null,
    codexAdapter: null,
    browserSockets: new Set(),
    state: {
      session_id: "test-session",
      model: "claude-4-opus",
      cwd: "/tmp",
      tools: [],
      permissionMode: "default",
      claude_code_version: "1.0.0",
      mcp_servers: [],
      agents: [],
      slash_commands: [],
      skills: [],
      total_cost_usd: 0,
      num_turns: 0,
      context_used_percent: 0,
      input_tokens: 0,
      output_tokens: 0,
      cache_read_tokens: 0,
      cache_creation_tokens: 0,
      context_window: 200000,
      is_compacting: false,
      git_branch: "main",
      is_worktree: false,
      is_containerized: false,
      repo_root: "/tmp",
      git_ahead: 0,
      git_behind: 0,
      total_lines_added: 0,
      total_lines_removed: 0,
      output_style: "text",
    },
    pendingPermissions: new Map(),
    pendingControlRequests: new Map(),
    messageHistory: [],
    pendingMessages: [],
    nextEventSeq: 1,
    eventBuffer: [],
    lastAckSeq: 0,
    processedClientMessageIds: [],
    processedClientMessageIdSet: new Set(),
    messageQueue: [],
    queuePaused: false,
    keepAliveInterval: null,
  } as unknown as Session;
}

describe("handleRewindFiles", () => {
  it("should send dry_run control request and resolve with result", async () => {
    const session = makeSession();

    const sendControlRequestFn = vi.fn() as Mock<SendControlRequestFn>;

    const resultPromise = handleRewindFiles(
      session,
      "msg-123",
      true,
      sendControlRequestFn,
    );

    expect(sendControlRequestFn).toHaveBeenCalledOnce();

    const [request, onResponse] = sendControlRequestFn.mock.calls[0] as Parameters<SendControlRequestFn>;
    expect(request).toEqual({
      subtype: "rewind_files",
      user_message_id: "msg-123",
      dry_run: true,
    });
    expect(onResponse).toBeDefined();
    expect(onResponse!.subtype).toBe("rewind_files");

    // Simulate CLI response
    const mockResult: RewindFilesResult = {
      canRewind: true,
      filesChanged: ["src/main.ts", "src/utils.ts"],
      insertions: 10,
      deletions: 5,
    };
    onResponse!.resolve(mockResult);

    const result = await resultPromise;
    expect(result).toEqual(mockResult);
  });

  it("should send actual rewind request (non-dry-run)", async () => {
    const session = makeSession();

    const sendControlRequestFn = vi.fn() as Mock<SendControlRequestFn>;

    const resultPromise = handleRewindFiles(
      session,
      "msg-456",
      false,
      sendControlRequestFn,
    );

    const [request, onResponse] = sendControlRequestFn.mock.calls[0] as Parameters<SendControlRequestFn>;
    expect(request).toEqual({
      subtype: "rewind_files",
      user_message_id: "msg-456",
      dry_run: false,
    });

    // Simulate success response
    onResponse!.resolve({ canRewind: true });

    const result = await resultPromise;
    expect(result.canRewind).toBe(true);
  });

  it("should resolve with canRewind: false on error", async () => {
    const session = makeSession();

    const sendControlRequestFn = vi.fn() as Mock<SendControlRequestFn>;

    const resultPromise = handleRewindFiles(
      session,
      "msg-789",
      true,
      sendControlRequestFn,
    );

    const [, onResponse] = sendControlRequestFn.mock.calls[0] as Parameters<SendControlRequestFn>;

    const errorResult: RewindFilesResult = {
      canRewind: false,
      error: "Message not found",
    };
    onResponse!.resolve(errorResult);

    const result = await resultPromise;
    expect(result.canRewind).toBe(false);
    expect(result.error).toBe("Message not found");
  });
});
