// @vitest-environment jsdom
//
// Contract test for the cockpit WorkingSpinner force-end-turn gate.
// Verifies the post-#1176 behaviour: the escape hatch is suppressed
// whenever any tool is in flight, the stalled-time label still flips
// so the user sees the wait, and the button only surfaces for a
// silent model with no tool running.

import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { act, cleanup, render, screen } from "@testing-library/react";

import { WorkingSpinner } from "../CockpitView";

function makeRef(initial: number): React.RefObject<number> {
  return { current: initial } as React.RefObject<number>;
}

beforeEach(() => {
  vi.useFakeTimers();
});

afterEach(() => {
  cleanup();
  vi.useRealTimers();
});

function renderSpinner(opts: {
  /** Seconds since the last streaming frame (drives the watchdog). */
  stalledSecs: number;
  /** In-flight tool name, or null for "model is silent". */
  tool: string | null;
  thinking?: boolean;
}) {
  const now = Date.now();
  const ref = makeRef(now - opts.stalledSecs * 1000);
  const onForceEndTurn = vi.fn().mockResolvedValue(undefined);
  render(
    <WorkingSpinner
      thinking={opts.thinking ?? false}
      tool={opts.tool}
      lastActivityRef={ref}
      onForceEndTurn={onForceEndTurn}
    />,
  );
  // One watchdog tick so the 1s interval inside the component picks
  // up the pre-seeded `lastActivityRef` and the label / button states
  // settle.
  act(() => {
    vi.advanceTimersByTime(1100);
  });
  return { onForceEndTurn };
}

describe("WorkingSpinner force-end-turn gate (#1176)", () => {
  it("hides the button while a tool is in flight, even past the stall threshold", () => {
    renderSpinner({ stalledSecs: 60, tool: "Write" });
    expect(
      screen.queryByRole("button", { name: /force end turn/i }),
    ).toBeNull();
    expect(screen.getByText(/waiting on tool…/i)).toBeTruthy();
  });

  it("shows the button when no tool is in flight past the stall threshold", () => {
    renderSpinner({ stalledSecs: 60, tool: null });
    expect(
      screen.getByRole("button", { name: /force end turn/i }),
    ).toBeTruthy();
    expect(screen.getByText(/waiting on model…/i)).toBeTruthy();
  });

  it("hides the button below threshold regardless of tool state", () => {
    renderSpinner({ stalledSecs: 5, tool: null });
    expect(
      screen.queryByRole("button", { name: /force end turn/i }),
    ).toBeNull();
    expect(screen.queryByText(/waiting on (model|tool)…/i)).toBeNull();
  });

  it("hides the button for a long-running Task subagent (original report)", () => {
    renderSpinner({ stalledSecs: 180, tool: "Task" });
    expect(
      screen.queryByRole("button", { name: /force end turn/i }),
    ).toBeNull();
    expect(screen.getByText(/waiting on tool… 3m \d{2}s/i)).toBeTruthy();
  });
});
