import { render, screen, act } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
import type { CheckinStatusData } from "@alqove/api-client";

const getStatus = vi.fn();
vi.mock("@/lib/checkin/api", () => ({
  checkinApi: { getStatus: (...a: unknown[]) => getStatus(...a) },
}));

import {
  StatusView,
  StatusPoller,
  statusRefetchInterval,
  POLL_INTERVAL_MS,
} from "../StatusPoller";

// Default = live (has a queue position), non-terminal. Pass `queue_position:
// undefined, estimated_wait_minutes: undefined` to model confirmation_only.
function status(overrides: Partial<CheckinStatusData> = {}): CheckinStatusData {
  return {
    status: "queued",
    status_label: "In line",
    container_count: 3,
    queue_position: 2,
    estimated_wait_minutes: 10,
    ...overrides,
  };
}

const confirmationOnly = () =>
  status({ queue_position: undefined, estimated_wait_minutes: undefined });

describe("statusRefetchInterval", () => {
  it("polls in live, non-terminal state", () => {
    expect(statusRefetchInterval(status())).toBe(POLL_INTERVAL_MS);
  });
  it("does not poll in confirmation_only mode (no position/ETA)", () => {
    expect(statusRefetchInterval(confirmationOnly())).toBe(false);
  });
  it("does not poll once terminal", () => {
    expect(statusRefetchInterval(status({ status: "accepted" }))).toBe(false);
  });
  it("does not poll without data", () => {
    expect(statusRefetchInterval(undefined)).toBe(false);
  });
});

describe("StatusView", () => {
  it("renders queue position + ETA in live mode", () => {
    render(
      <StatusView
        status={status({ queue_position: 3, estimated_wait_minutes: 20 })}
      />,
    );
    expect(screen.getByText(/#3/)).toBeInTheDocument();
    expect(screen.getByText(/~20 min/)).toBeInTheDocument();
  });

  it("renders a static confirmation with no position in confirmation_only mode", () => {
    render(<StatusView status={confirmationOnly()} />);
    expect(screen.getByText(/you're checked in/i)).toBeInTheDocument();
    expect(screen.queryByText(/#/)).not.toBeInTheDocument();
  });

  it("renders the terminal state", () => {
    render(<StatusView status={status({ status: "accepted" })} />);
    expect(screen.getByText(/all done/i)).toBeInTheDocument();
    expect(screen.getByText(/complete/i)).toBeInTheDocument();
  });
});

describe("StatusPoller slow polling", () => {
  beforeEach(() => {
    getStatus.mockReset();
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.useRealTimers();
  });

  function renderPoller(initial: CheckinStatusData) {
    const qc = new QueryClient({
      defaultOptions: {
        queries: {
          retry: false,
          staleTime: Infinity,
          refetchOnWindowFocus: false,
        },
      },
    });
    render(
      <QueryClientProvider client={qc}>
        <StatusPoller statusToken="st123" initialStatus={initial} />
      </QueryClientProvider>,
    );
  }

  it("polls on the interval in live mode and stops once terminal", async () => {
    // Mutable current response — avoids brittle call-order coupling.
    let current = status({ queue_position: 2 });
    getStatus.mockImplementation(async () => ({ data: current }));

    renderPoller(status({ queue_position: 3 }));
    expect(getStatus).not.toHaveBeenCalled();

    await act(async () => {
      await vi.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
    });
    expect(getStatus).toHaveBeenCalledTimes(1);

    // Next poll returns a terminal status — the interval recomputes from the
    // fetched data and must stop. (Rendering of each state is covered by the
    // StatusView tests above.)
    current = status({ status: "accepted" });
    await act(async () => {
      await vi.advanceTimersByTimeAsync(POLL_INTERVAL_MS);
    });
    expect(getStatus).toHaveBeenCalledTimes(2);

    // Terminal — polling must have stopped; no further fetches.
    await act(async () => {
      await vi.advanceTimersByTimeAsync(POLL_INTERVAL_MS * 3);
    });
    expect(getStatus).toHaveBeenCalledTimes(2);
  });

  it("never polls in confirmation_only mode", async () => {
    renderPoller(confirmationOnly());
    await act(async () => {
      await vi.advanceTimersByTimeAsync(POLL_INTERVAL_MS * 3);
    });
    expect(getStatus).not.toHaveBeenCalled();
  });
});
