// @vitest-environment happy-dom
// TailStatusPill is a leaf component with no async state and no hooks
// beyond a single setInterval — happy-dom is the right env so we can
// inspect the rendered DOM.
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

import React, { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { TailStatusPill } from "./TailStatusPill";
import type { TailStatus } from "@/hooks/useTailStream";

/**
 * Minimal render helper — the project doesn't ship
 * `@testing-library/react`, so we render straight into a real
 * `createRoot` and inspect the DOM via `container.innerHTML` and
 * `container.querySelector`.
 *
 * Note: React 18's `createRoot` defers DOM mutation until the calling
 * microtask flushes unless we wrap the render in `act`. The pill's only
 * effect is a `setInterval`, so we use `act(() => { root.render(node) })`
 * — sync act is fine because no state updates fire during render.
 */
function renderInto(node: React.ReactElement): {
  container: HTMLDivElement;
  root: Root;
  unmount: () => void;
} {
  const container = document.createElement("div");
  document.body.appendChild(container);
  const root = createRoot(container);
  act(() => {
    root.render(node);
  });
  return {
    container,
    root,
    unmount: () => {
      act(() => root.unmount());
      container.remove();
    },
  };
}

const ALL_STATUSES: TailStatus[] = [
  "connecting",
  "live",
  "reconnecting",
  "closed",
  "stalled",
  "error",
];

describe("TailStatusPill", () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-06-20T12:00:30Z"));
  });

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

  it("test_renders_badge_for_each_status", () => {
    // Iterate every TailStatus and assert no crash, plus a sensible label.
    // The plan's smoke spec calls for "renders all 5 statuses"; we test
    // all 6 (closed is also a status even if rare in practice).
    for (const status of ALL_STATUSES) {
      const { container, root, unmount } = renderInto(
        <TailStatusPill
          status={status}
          lastEventAt={null}
          onReconnect={() => {}}
        />,
      );

      // The pill root should be present.
      const pill = container.querySelector('[data-testid="tail-status-pill"]');
      expect(pill).not.toBeNull();

      // The Badge child should carry the human label — match by text
      // content (the badge text is the only handoff the user reads).
      const expectedLabels: Record<TailStatus, string> = {
        connecting: "Connecting",
        live: "Live",
        reconnecting: "Reconnecting",
        closed: "Closed",
        stalled: "Stalled",
        error: "Error",
      };
      expect(pill?.textContent).toContain(expectedLabels[status]);

      unmount();
      // Silence "unmount was not wrapped in act" warnings; renderInto's
      // unmount is a sync root.unmount() and React tolerates this for
      // leaf components with no async state.
      void root;
    }
  });

  it("test_shows_reconnect_button_only_for_stalled_and_error", () => {
    const onReconnect = vi.fn();

    for (const status of ALL_STATUSES) {
      const { container, unmount } = renderInto(
        <TailStatusPill
          status={status}
          lastEventAt={null}
          onReconnect={onReconnect}
        />,
      );

      const button = container.querySelector(
        '[data-testid="tail-status-pill-reconnect"]',
      );

      if (status === "stalled" || status === "error") {
        expect(button).not.toBeNull();
        expect(button?.textContent).toContain("Reconnect");
      } else {
        expect(button).toBeNull();
      }

      unmount();
    }
  });

  it("test_subtitle_says_never_when_lastEventAt_is_null", () => {
    const { container, unmount } = renderInto(
      <TailStatusPill
        status="live"
        lastEventAt={null}
        onReconnect={() => {}}
      />,
    );

    const subtitle = container.querySelector(
      '[data-testid="tail-status-pill-subtitle"]',
    );
    expect(subtitle).not.toBeNull();
    expect(subtitle?.textContent).toBe("Last event: never");

    unmount();
  });

  it("test_subtitle_shows_age_in_seconds_when_lastEventAt_recent", () => {
    // 12 seconds ago — fake-timer driven so the assertion is stable.
    const eventAt = new Date("2026-06-20T12:00:18Z");
    const { container, unmount } = renderInto(
      <TailStatusPill
        status="live"
        lastEventAt={eventAt}
        onReconnect={() => {}}
      />,
    );

    const subtitle = container.querySelector(
      '[data-testid="tail-status-pill-subtitle"]',
    );
    expect(subtitle?.textContent).toBe("Last event: 12s ago");

    unmount();
  });

  it("test_reconnect_button_invokes_onReconnect_when_clicked", () => {
    const onReconnect = vi.fn();
    const { container, unmount } = renderInto(
      <TailStatusPill
        status="stalled"
        lastEventAt={null}
        onReconnect={onReconnect}
      />,
    );

    const button = container.querySelector(
      '[data-testid="tail-status-pill-reconnect"]',
    ) as HTMLButtonElement | null;
    expect(button).not.toBeNull();
    button?.click();
    expect(onReconnect).toHaveBeenCalledTimes(1);

    unmount();
  });
});
