// PerNodeDistribution: rows render with util bars; pressure above 85% is
// carried by the bars alone. Rows are click-to-expand, revealing the node's
// live usage chart (fetcher mocked; its own spec covers the chart).
// Fabricated PerNodeDistributionRow fixtures.

import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import { PerNodeDistribution } from "@/components/regions/PerNodeDistribution";
import type { PerNodeDistributionRow } from "@/engine/report-model";

vi.mock("@/lib/api-client", () => ({
  fetchNodeUsage: vi.fn(async () => ({ points: [] })),
}));

function mkRow(overrides: Partial<PerNodeDistributionRow>): PerNodeDistributionRow {
  return {
    node: "ip-10-0-0-1",
    nodegroup: "ng-a",
    podCount: 8,
    reqCpu: 1.5,
    reqMemGib: 6,
    allocatableCpu: 3.92,
    allocatableMemGib: 14.28,
    pctC: 38,
    pctM: 42,
    flagC: false,
    flagM: false,
    ...overrides,
  };
}

const rows: PerNodeDistributionRow[] = [
  mkRow({}),
  mkRow({
    node: "ip-10-0-0-2",
    nodegroup: "ng-b",
    podCount: 3,
    reqCpu: 3.7,
    reqMemGib: 13,
    pctC: 94.4,
    pctM: 91,
    flagC: true,
    flagM: true,
  }),
];

const props = { rows, regionKey: "aws-us-west-2" };

describe("PerNodeDistribution", () => {
  it("renders node, nodegroup, pod count and util bar percentages", () => {
    render(<PerNodeDistribution {...props} />);
    expect(screen.getByText("ip-10-0-0-1")).toBeTruthy();
    expect(screen.getByText("ip-10-0-0-2")).toBeTruthy();
    expect(screen.getByText("ng-a")).toBeTruthy();
    expect(screen.getByText("94%")).toBeTruthy(); // UtilBar rounds 94.4
    expect(screen.getByText("91%")).toBeTruthy();
  });

  it("expands a row on click to reveal the node usage chart, and collapses again", () => {
    render(<PerNodeDistribution {...props} />);
    // jsdom renders the chart's zero-size containers; the loading state is
    // the expand witness.
    fireEvent.click(screen.getByText("ip-10-0-0-1"));
    expect(screen.getByText("loading node usage…")).toBeTruthy();
    // One row expanded at a time: expanding the other collapses the first.
    fireEvent.click(screen.getByText("ip-10-0-0-2"));
    expect(screen.getAllByText("loading node usage…").length).toBe(1);
    fireEvent.click(screen.getByText("ip-10-0-0-2"));
    expect(screen.queryByText("loading node usage…")).toBeNull();
  });

  it("expands via keyboard (Enter) on the focused row", () => {
    render(<PerNodeDistribution {...props} />);
    const row = screen.getByText("ip-10-0-0-1").closest("tr")!;
    fireEvent.keyDown(row, { key: "Enter" });
    expect(screen.getByText("loading node usage…")).toBeTruthy();
  });

  it("renders the empty state without rows", () => {
    render(<PerNodeDistribution rows={[]} regionKey="aws-us-west-2" />);
    expect(screen.getByText(/No per-node distribution/)).toBeTruthy();
  });
});

describe("PerNodeDistribution max-usage columns", () => {
  it("renders max values and their share bars; missing maxes render dashes", () => {
    render(
      <PerNodeDistribution
        rows={[
          mkRow({ maxCpuCores: 3.1, maxMemGib: 12.4, maxPctC: 79, maxPctM: 87 }),
          mkRow({ node: "ip-10-0-0-9", maxCpuCores: null, maxMemGib: null }),
        ]}
        regionKey="aws-us-west-2"
      />,
    );
    expect(screen.getByText("3.10 cores")).toBeTruthy();
    expect(screen.getByText("12.40 GiB")).toBeTruthy();
    // 3.1 / 3.92 allocatable ≈ 79% — the bar shows the share.
    expect(screen.getByTitle("max CPU vs allocatable")).toBeTruthy();
    expect(screen.getByTitle("max memory vs allocatable")).toBeTruthy();
    // Null maxes: dashes, no bar.
    const dashRow = screen.getByText("ip-10-0-0-9").closest("tr")!;
    const dashes = [...dashRow.querySelectorAll("td")].filter((td) => td.textContent === "—");
    expect(dashes.length).toBeGreaterThanOrEqual(4); // 2 values + 2 bars
    expect(dashRow.querySelectorAll('[title="max CPU vs allocatable"]').length).toBe(0);
  });
});
