import { beforeEach, describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import type { ButtonHTMLAttributes, ReactNode } from "react";
import { MAX_PANEL_COUNT, MissionControlPanel, type PanelConfig } from "./MissionControlPanel";

interface MockButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  children: ReactNode;
}

interface MockOrchestratorPanelProps {
  sessionId?: string;
  onClose?: () => void;
  closeDisabled?: boolean;
  onExpand?: () => void;
  expanded?: boolean;
}

const mockOrchestratorPanel = vi.fn<(props: MockOrchestratorPanelProps) => React.JSX.Element>();

vi.mock("@/components/mission-control/OrchestratorPanel", () => ({
  OrchestratorPanel: (props: MockOrchestratorPanelProps): React.JSX.Element =>
    mockOrchestratorPanel(props),
}));

vi.mock("@/components/ui/button", () => ({
  Button: ({ children, ...props }: MockButtonProps): React.JSX.Element => (
    <button {...props}>{children}</button>
  ),
}));

function buildPanels(count: number): PanelConfig[] {
  return Array.from({ length: count }, (_, index) => ({
    sessionId: `session-${String(index + 1)}`,
  }));
}

describe("MissionControlPanel", (): void => {
  beforeEach((): void => {
    vi.clearAllMocks();

    mockOrchestratorPanel.mockImplementation(
      ({ sessionId, closeDisabled, expanded }: MockOrchestratorPanelProps): React.JSX.Element => (
        <div
          data-testid="orchestrator-panel"
          data-session-id={sessionId ?? ""}
          data-close-disabled={String(closeDisabled ?? false)}
          data-expanded={String(expanded ?? false)}
        />
      )
    );
  });

  it("renders the panel grid and default heading", (): void => {
    render(
      <MissionControlPanel
        panels={[{}]}
        onAddPanel={vi.fn<() => void>()}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={vi.fn<(index: number) => void>()}
      />
    );

    expect(screen.getByRole("heading", { name: "Panels" })).toBeInTheDocument();
    expect(screen.getAllByTestId("orchestrator-panel")).toHaveLength(1);
  });

  it("calls onAddPanel when the add button is clicked", (): void => {
    const onAddPanel = vi.fn<() => void>();

    render(
      <MissionControlPanel
        panels={[{}]}
        onAddPanel={onAddPanel}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={vi.fn<(index: number) => void>()}
      />
    );

    fireEvent.click(screen.getByRole("button", { name: "Add panel" }));

    expect(onAddPanel).toHaveBeenCalledTimes(1);
  });

  it("disables add panel at the configured maximum", (): void => {
    render(
      <MissionControlPanel
        panels={buildPanels(MAX_PANEL_COUNT)}
        onAddPanel={vi.fn<() => void>()}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={vi.fn<(index: number) => void>()}
      />
    );

    const addButton = screen.getByRole("button", { name: "Add panel" });

    expect(addButton).toBeDisabled();
    expect(addButton).toHaveAttribute("title", "Maximum of 6 panels");
  });

  it("passes closeDisabled=false when more than one panel exists", (): void => {
    render(
      <MissionControlPanel
        panels={buildPanels(2)}
        onAddPanel={vi.fn<() => void>()}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={vi.fn<(index: number) => void>()}
      />
    );

    const renderedPanels = screen.getAllByTestId("orchestrator-panel");
    expect(renderedPanels).toHaveLength(2);

    for (const panel of renderedPanels) {
      expect(panel).toHaveAttribute("data-close-disabled", "false");
    }
  });

  it("renders only the expanded panel in focused mode", (): void => {
    render(
      <MissionControlPanel
        panels={[{ sessionId: "session-1" }, { sessionId: "session-2", expanded: true }]}
        onAddPanel={vi.fn<() => void>()}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={vi.fn<(index: number) => void>()}
      />
    );

    const renderedPanels = screen.getAllByTestId("orchestrator-panel");

    expect(renderedPanels).toHaveLength(1);
    expect(renderedPanels[0]).toHaveAttribute("data-session-id", "session-2");
    expect(renderedPanels[0]).toHaveAttribute("data-expanded", "true");
  });

  it("handles Escape key by toggling expanded panel", async (): Promise<void> => {
    const onExpandPanel = vi.fn<(index: number) => void>();

    render(
      <MissionControlPanel
        panels={[{ sessionId: "session-1", expanded: true }, { sessionId: "session-2" }]}
        onAddPanel={vi.fn<() => void>()}
        onRemovePanel={vi.fn<(index: number) => void>()}
        onExpandPanel={onExpandPanel}
      />
    );

    fireEvent.keyDown(window, { key: "Escape" });

    await waitFor((): void => {
      expect(onExpandPanel).toHaveBeenCalledWith(0);
    });
  });
});
