// PrerequisiteTable: the conditional scenario's prerequisite worklist —
// string prerequisites joined with workload rows into current -> target
// requests, grouped by the engine's component groups (same order and
// vmcluster-expanded default as the Workloads tab), impact-sorted inside
// each group, idle removals separated.

import { describe, expect, it } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import { PrerequisiteTable } from "@/components/regions/PrerequisiteTable";
import type { WorkloadRow } from "@/engine/classify";

function w(over: Partial<WorkloadRow> = {}): WorkloadRow {
  return {
    ns: "observability",
    kind: "StatefulSet",
    name: "vmstorage-vmcluster",
    group: "vmcluster",
    npods: 3,
    // WorkloadRow requests are the workload TOTAL across replicas
    // (workloads.ts sums per-pod requests) — 3 pods x 4c / 70g here.
    reqCpu: 12,
    reqMemGib: 210,
    newReqCpu: 1.5,
    newReqMemGib: 49,
    ...over,
  } as WorkloadRow;
}

describe("PrerequisiteTable", () => {
  it("divides the workload-total request by replicas before comparing to the per-pod target", () => {
    render(
      <PrerequisiteTable
        prerequisites={["requests:observability/StatefulSet/vmstorage-vmcluster:cpu=1.5,mem=49"]}
        workloads={[w()]}
      />,
    );
    // Per-pod now = 12/3 cores, 210/3 GiB — NOT the workload totals.
    expect(screen.getByText("4.00c / 70.0g")).toBeInTheDocument();
    expect(screen.getByText("1.50c / 49.0g")).toBeInTheDocument();
    // Freed = (4 - 1.5) * 3 cores, (70 - 49) * 3 GiB — counted once per
    // replica. The figure appears twice: the summary sentence and the
    // VMCluster group header subtotal.
    expect(screen.getByText("7.50c / 63.0g")).toBeInTheDocument();
    expect(screen.getAllByText(/7\.5c \/ 63\.0g/).length).toBe(2);
    expect(screen.getByText(/1 request adjustment \+ 0 idle removals/)).toBeInTheDocument();
    expect(screen.getByText("vmstorage-vmcluster")).toBeInTheDocument();
    expect(screen.getByText("observability · StatefulSet")).toBeInTheDocument();
    // vmcluster renders expanded by default under its display label.
    expect(screen.getByText(/▾ VMCluster/)).toBeInTheDocument();
  });

  it("renders idle removals as their own table with a delete action", () => {
    render(
      <PrerequisiteTable
        prerequisites={[
          "remove-idle:metrics-integration/Deployment/metrics-integration-regional-service",
        ]}
        workloads={[
          w({
            ns: "metrics-integration",
            kind: "Deployment",
            name: "metrics-integration-regional-service",
            group: "vmcluster",
            npods: 2,
            reqCpu: 1, // total: 2 pods x 0.5c
            reqMemGib: 2, // total: 2 pods x 1g
          }),
        ]}
      />,
    );
    expect(screen.getByText(/0 request adjustments \+ 1 idle removal/)).toBeInTheDocument();
    expect(screen.getByText("delete or scale to zero")).toBeInTheDocument();
    expect(screen.getByText("0.50c / 1.0g")).toBeInTheDocument();
  });

  it("groups rows like the Workloads tab: engine rank order, impact sort inside, collapsed until clicked", () => {
    render(
      <PrerequisiteTable
        prerequisites={[
          "requests:ns/StatefulSet/vmstorage-vmcluster:cpu=1,mem=8",
          "requests:ns/Deployment/small-loki:cpu=0.5,mem=1",
          "requests:ns/Deployment/big-loki:cpu=0.5,mem=2",
        ]}
        workloads={[
          w({ ns: "ns", npods: 2, reqCpu: 8, reqMemGib: 20 }),
          w({ ns: "ns", kind: "Deployment", name: "small-loki", group: "loki", npods: 1, reqCpu: 1, reqMemGib: 2 }),
          w({ ns: "ns", kind: "Deployment", name: "big-loki", group: "loki", npods: 1, reqCpu: 2, reqMemGib: 4 }),
        ]}
      />,
    );
    // Group headers appear in engine rank order: VMCluster before Loki.
    const headers = screen.getAllByText(/VMCluster|Loki/);
    expect(headers[0]!.textContent).toMatch(/VMCluster/);
    expect(headers[1]!.textContent).toMatch(/Loki/);
    // vmcluster rows are expanded by default.
    expect(screen.getByText("vmstorage-vmcluster")).toBeInTheDocument();
    // Loki rows are hidden until the group header is clicked.
    expect(screen.queryByText("big-loki")).not.toBeInTheDocument();
    fireEvent.click(headers[1]!);
    // Within the group, the bigger freed-memory row sorts first.
    const names = screen.getAllByText(/loki/);
    expect(names[0]!.textContent).toBe("big-loki");
    expect(names[1]!.textContent).toBe("small-loki");
  });

  it("tolerates a prerequisite whose workload row is missing (an Other bucket)", () => {
    render(
      <PrerequisiteTable
        prerequisites={["requests:ns/Deployment/ghost:cpu=0.1,mem=0.5"]}
        workloads={[]}
      />,
    );
    // Ghost rows land in the collapsed "Other" group — open it.
    const header = screen.getByText(/Other/);
    expect(screen.queryByText("ghost")).not.toBeInTheDocument();
    fireEvent.click(header);
    expect(screen.getByText("ghost")).toBeInTheDocument();
    expect(screen.getAllByText("—").length).toBeGreaterThan(0);
    // The totals live on their own span (the summary sentence spans nodes).
    expect(screen.getByText("0.0c / 0.0g")).toBeInTheDocument();
  });

  it("renders unrecognized prerequisite strings verbatim instead of dropping them", () => {
    render(
      <PrerequisiteTable
        prerequisites={["retire:apps/Deployment/old-web"]}
        workloads={[]}
      />,
    );
    // An unknown format must stay visible — an empty worklist may never
    // mean "the parser gave up".
    expect(screen.getByText(/retire:apps\/Deployment\/old-web/)).toBeInTheDocument();
  });

  it("renders nothing without prerequisites", () => {
    const { container } = render(<PrerequisiteTable prerequisites={[]} workloads={[w()]} />);
    expect(container).toBeEmptyDOMElement();
  });
});
