// ScenarioComparison: the shrink recommendation panel over a fabricated
// ShrinkEvaluation — primary + conditional cards, the prerequisite
// worklist, manual-review alternatives, and the empty state when nothing
// is feasible.

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

function mkScenario(overrides: Partial<ShrinkScenario>): ShrinkScenario {
  return {
    id: "remove:g:1:immediate",
    fingerprint: "v1|remove|immediate|g@a:i(2)->i(1)|",
    action: "remove",
    mode: "immediate",
    status: "reviewRequired",
    changes: [
      { nodeGroup: "g", zone: "a", fromType: "i.large", toType: "i.large", fromCount: 2, toCount: 1 },
    ],
    nodeCountBefore: 4,
    nodeCountAfter: 3,
    computeMonthlyCostUsd: 180,
    computeMonthlySavingsUsd: 120,
    computeSavingsPercent: 40,
    disruption: { nodesRemoved: 1, nodesReplaced: 0, statelessPodsAffected: 2, statefulPodsAffected: 0, criticalPodsAffected: 0 },
    utilization: { requestCpuPct: 50, requestMemPct: 40, safeDemandCpuPct: 60, safeDemandMemPct: 50 },
    prerequisites: [],
    manualChecks: ["schedulingSemantics"],
    ...overrides,
  };
}

function mkEvaluation(overrides: Partial<ShrinkEvaluation>): ShrinkEvaluation {
  return {
    schemaVersion: 3,
    searchComplete: true,
    baseline: { nodeCount: 4, computeMonthlyCostUsd: 300, unpricedTypes: [] },
    primary: mkScenario({}),
    conditional: null,
    rejected: [
      {
        id: "remove:h:1",
        action: "remove",
        mode: "immediate",
        computeMonthlyCostUsd: null,
        constraint: "unpriced",
        detail: "changed types unpriced: t3.small",
      },
    ],
    alternatives: [
      { fromInstanceType: "i.large", toInstanceType: "j.large", pricePerNodeUsd: 42 },
    ],
    ...overrides,
  };
}

describe("ScenarioComparison (shrink recommendation panel)", () => {
  it("renders the primary recommendation with status, streak, and savings", () => {
    render(<ScenarioComparison evaluation={mkEvaluation({})} streak={2} workloads={[]} />);
    expect(screen.getByText(/stability 2\/3/)).toBeInTheDocument();
    expect(screen.getByText(/needs manual verification/)).toBeInTheDocument();
    expect(screen.getByText(/g @ a: 2 → 1 × i\.large/)).toBeInTheDocument();
    expect(screen.getByText(/\$120\.00\/mo savings/)).toBeInTheDocument();
  });

  it("renders conditional prerequisites as a current -> target worklist", () => {
    render(
      <ScenarioComparison
        evaluation={mkEvaluation({
          conditional: mkScenario({
            id: "x:conditional",
            mode: "conditional",
            prerequisites: ["requests:apps/Deployment/web:cpu=0.25,mem=1"],
          }),
        })}
        streak={null}
        workloads={[
          {
            ns: "apps",
            kind: "Deployment",
            name: "web",
            group: "vmcluster",
            npods: 2,
            // totals: 2 pods x 1c / 2g
            reqCpu: 2,
            reqMemGib: 4,
            newReqCpu: 0.25,
            newReqMemGib: 1,
          } as WorkloadRow,
        ]}
      />,
    );
    expect(screen.getByText(/Conditional opportunity/)).toBeInTheDocument();
    expect(screen.getByText(/1 request adjustment \+ 0 idle removals/)).toBeInTheDocument();
    // Current -> target rendered as pairs, not the raw engine string.
    expect(screen.getByText("1.00c / 2.0g")).toBeInTheDocument();
    expect(screen.getByText("0.25c / 1.0g")).toBeInTheDocument();
  });

  it("shows manual-review alternatives", () => {
    render(<ScenarioComparison evaluation={mkEvaluation({})} streak={null} workloads={[]} />);
    expect(screen.getByText(/i\.large → j\.large/)).toBeInTheDocument();
  });

  it("empty state when no primary recommendation exists", () => {
    render(
      <ScenarioComparison evaluation={mkEvaluation({ primary: null })} streak={null} workloads={[]} />,
    );
    expect(screen.getByText(/No immediate recommendation/)).toBeInTheDocument();
  });

  it("flags a capped search", () => {
    render(
      <ScenarioComparison
        evaluation={mkEvaluation({ searchComplete: false })}
        workloads={[]}
        streak={null}
      />,
    );
    expect(screen.getByText(/combination cap/)).toBeInTheDocument();
  });
});
