// ReductionOpportunities: rows filter to actionable nodegroups
// (couldRemove > 0 or a candidate flag), estimates badge by source, and
// reduce/downsize actions mark the candidate rows. Fabricated
// NodegroupReductionRow fixtures (only the rendered fields vary).

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

function mkNgRow(over: Partial<NodegroupReductionRow> = {}): NodegroupReductionRow {
  return {
    ngName: "ng-a",
    instanceType: "r6i.xlarge",
    capacityType: null,
    count: 3,
    allocCpu: 32,
    allocMem: 128,
    curCpu: 10,
    curMem: 40,
    newCpu: 8,
    newMem: 32,
    dsCpuTotal: 2,
    dsMemTotal: 8,
    netAllocCpu: 30,
    netAllocMem: 120,
    newUtilCpu: 26.7,
    newUtilMem: 26.7,
    perNodeAllocCpu: 8,
    perNodeAllocMem: 32,
    freeCpu: 4,
    freeMem: 16,
    cpuRm: 2,
    memRm: 2,
    ratioRemove: 2,
    couldRemove: 2,
    estimateSource: "scenario",
    reductionCandidate: false,
    downsizeCandidate: false,
    ...over,
  };
}

function renderTable(rows: NodegroupReductionRow[]): ReturnType<typeof render> {
  return render(
    <ReductionOpportunities
      data={{
        nodegroups: rows,
        reductionCandidates: rows.filter((r) => r.reductionCandidate),
        downsizeCandidates: rows.filter((r) => r.downsizeCandidate),
        actionNames: [],
      }}
    />,
  );
}

describe("ReductionOpportunities", () => {
  it("renders only actionable nodegroups", () => {
    renderTable([
      mkNgRow({ ngName: "ng-actionable" }), // couldRemove > 0
      mkNgRow({ ngName: "ng-candidate", couldRemove: 0, downsizeCandidate: true }),
      mkNgRow({ ngName: "ng-quiet", couldRemove: 0 }), // no flag, nothing to remove
    ]);
    expect(screen.getByText("ng-actionable")).toBeInTheDocument();
    expect(screen.getByText("ng-candidate")).toBeInTheDocument();
    expect(screen.queryByText("ng-quiet")).toBeNull();
  });

  it("renders the empty state when nothing is actionable", () => {
    renderTable([mkNgRow({ couldRemove: 0 })]);
    expect(
      screen.getByText("No nodegroup reduction opportunities — capacity matches demand."),
    ).toBeInTheDocument();
  });

  it("badges scenario estimates as info and ratio estimates as neutral", () => {
    renderTable([
      mkNgRow({ ngName: "ng-scenario", estimateSource: "scenario" }),
      mkNgRow({ ngName: "ng-ratio", estimateSource: "ratio" }),
    ]);
    expect(screen.getByText("scenario")).toHaveClass("bg-accent/10");
    expect(screen.getByText("ratio")).toHaveClass("bg-text-muted/10");
  });

  it("marks reduction and downsize candidates with action badges", () => {
    renderTable([
      mkNgRow({ ngName: "ng-both", reductionCandidate: true, downsizeCandidate: true }),
    ]);
    expect(screen.getByText("reduce")).toHaveClass("bg-success/10");
    expect(screen.getByText("downsize")).toHaveClass("bg-accent/10");
  });
});
