import { describe, expect, it } from "vitest";
import type { Node } from "@xyflow/react";
import type { EntropykNodeData } from "./configBuilder";
import {
  applyOverride,
  buildSweepCases,
  defaultSweepsFromDiagram,
  discoverSweepTargets,
  getSweepWarnings,
  parseValueList,
  suggestValues,
  type SweepTarget,
} from "./multiRun";

function node(
  type: string,
  name: string,
  params: Record<string, number | string | boolean>,
): Node<EntropykNodeData> {
  return {
    id: name,
    type: "entropykNode",
    position: { x: 0, y: 0 },
    data: { type, name, circuit: 0, rotation: 0, params },
  };
}

describe("multiRun", () => {
  it("parses value lists", () => {
    expect(parseValueList("1, 2; 3\n4")).toEqual(["1", "2", "3", "4"]);
  });

  it("applies brine water temperature overrides by component name", () => {
    const base = {
      fluid: "R134a",
      circuits: [
        {
          id: 0,
          components: [
            { type: "BrineSource", name: "evap_water_in", t_set_c: 12 },
            { type: "BrineSource", name: "cond_water_in", t_set_c: 30 },
          ],
        },
      ],
    };
    const next = applyOverride(base, "evap_water_in.t_set_c", "10", "scalar") as typeof base;
    expect(next.circuits[0].components[0].t_set_c).toBe(10);
    expect(next.circuits[0].components[1].t_set_c).toBe(30);
  });

  it("discovers water temperatures from BrineSource nodes first", () => {
    const nodes = [
      node("FloodedEvaporator", "evap", { ua: 8000 }),
      node("BrineSource", "evap_water_in", { t_set_c: 12, m_flow_kg_s: 0.5, p_set_bar: 2 }),
      node("BrineSource", "cond_water_in", { t_set_c: 30, m_flow_kg_s: 0.6, p_set_bar: 2 }),
    ];
    const targets = discoverSweepTargets(nodes, "R134a");
    const paths = targets.map((t) => t.path);
    expect(paths).toContain("evap_water_in.t_set_c");
    expect(paths).toContain("cond_water_in.t_set_c");
    expect(paths.indexOf("evap_water_in.t_set_c")).toBeLessThan(paths.indexOf("evap.ua"));
    expect(targets.find((t) => t.path === "evap_water_in.t_set_c")?.label).toMatch(/évaporateur/i);
  });

  it("defaults multi-run axes to both water temperatures", () => {
    const nodes = [
      node("BrineSource", "evap_water_in", { t_set_c: 12, m_flow_kg_s: 0.5 }),
      node("BrineSource", "cond_water_in", { t_set_c: 30, m_flow_kg_s: 0.6 }),
    ];
    const sweeps = defaultSweepsFromDiagram(nodes, "R134a");
    expect(sweeps).toHaveLength(2);
    expect(sweeps.map((s) => s.path).sort()).toEqual([
      "cond_water_in.t_set_c",
      "evap_water_in.t_set_c",
    ].sort());
    expect(sweeps[0].valuesText).toContain(",");
  });

  it("suggests temperature steps around current", () => {
    expect(suggestValues(12, "t_set_c")).toBe("10, 12, 14");
  });

  it("builds cartesian product of water temp sweeps", () => {
    const base = {
      fluid: "R134a",
      circuits: [
        {
          components: [
            { name: "evap_water_in", t_set_c: 12 },
            { name: "cond_water_in", t_set_c: 30 },
          ],
        },
      ],
    };
    const cases = buildSweepCases(base, [
      {
        path: "evap_water_in.t_set_c",
        label: "T évap",
        kind: "scalar",
        valuesText: "10, 12",
      },
      {
        path: "cond_water_in.t_set_c",
        label: "T cond",
        kind: "scalar",
        valuesText: "30, 35",
      },
    ]);
    expect(cases).toHaveLength(4);
    const cfg = cases.find((c) => c.label.includes("10") && c.label.includes("35"))
      ?.config as typeof base;
    expect(cfg.circuits[0].components[0].t_set_c).toBe(10);
    expect(cfg.circuits[0].components[1].t_set_c).toBe(35);
  });

  it("discovers fan_speed on MchxCondenserCoil (previously absent)", () => {
    const nodes = [node("MchxCondenserCoil", "coil", { fan_speed: 0.8 })];
    const targets = discoverSweepTargets(nodes, "R134a");
    const fanTarget = targets.find((t) => t.paramKey === "fan_speed");
    expect(fanTarget).toBeDefined();
    expect(fanTarget?.componentType).toBe("MchxCondenserCoil");
    expect(fanTarget?.group).toBe("machine");
    expect(fanTarget?.path).toBe("coil.fan_speed");
  });

  it("returns no orifice warning for an ExpansionValve.opening sweep", () => {
    const target: SweepTarget = {
      path: "vx.opening",
      label: "VX — opening",
      kind: "scalar",
      group: "machine",
      componentName: "vx",
      componentType: "ExpansionValve",
      paramKey: "opening",
    };
    const nodes = [
      node("ExpansionValve", "vx", { opening: 0.5, beta_m2: 1e-6, flow_model: "orifice" }),
    ];
    const baseConfig = {
      circuits: [
        {
          components: [
            { name: "vx", type: "ExpansionValve", opening: 0.5, beta_m2: 1e-6, flow_model: "orifice" },
          ],
        },
      ],
    };
    const warnings = getSweepWarnings(target, nodes, baseConfig);
    expect(warnings).toEqual([]);
  });

  it("warns when EXV opening has no orifice_kv", () => {
    const target: SweepTarget = {
      path: "exv.opening",
      label: "EXV — opening",
      kind: "scalar",
      group: "machine",
      componentName: "exv",
      componentType: "IsenthalpicExpansionValve",
      paramKey: "opening",
    };
    const nodes = [node("IsenthalpicExpansionValve", "exv", { opening: 0.5 })];
    const baseConfig = {
      circuits: [
        { components: [{ name: "exv", type: "IsenthalpicExpansionValve", opening: 0.5 }] },
      ],
    };
    const warnings = getSweepWarnings(target, nodes, baseConfig);
    expect(warnings).toContain(
      "opening n'a aucun effet sans orifice_kv (orifice absent ou ≤ 0)",
    );
  });

  it("warns when EXV opening is Free (solver actuator)", () => {
    const target: SweepTarget = {
      path: "exv.opening",
      label: "EXV — opening",
      kind: "scalar",
      group: "machine",
      componentName: "exv",
      componentType: "IsenthalpicExpansionValve",
      paramKey: "opening",
    };
    const nodes = [
      node("IsenthalpicExpansionValve", "exv", {
        opening: 0.5,
        orifice_kv: 2e-6,
        __fixed_opening: false,
      }),
    ];
    const baseConfig = {
      circuits: [
        {
          components: [
            { name: "exv", type: "IsenthalpicExpansionValve", opening: 0.5, orifice_kv: 2e-6 },
          ],
        },
      ],
    };
    const warnings = getSweepWarnings(target, nodes, baseConfig);
    expect(warnings).toContain(
      "opening est libre (solver) — la valeur balayée sera ignorée. Passe-le en Fixed pour imposer la valeur.",
    );
    expect(warnings).toHaveLength(1);
  });

  it("returns no warning for a healthy fixed-orifice EXV", () => {
    const target: SweepTarget = {
      path: "exv.opening",
      label: "EXV — opening",
      kind: "scalar",
      group: "machine",
      componentName: "exv",
      componentType: "IsenthalpicExpansionValve",
      paramKey: "opening",
    };
    const nodes = [
      node("IsenthalpicExpansionValve", "exv", {
        opening: 0.5,
        orifice_kv: 2e-6,
        __fixed_opening: true,
      }),
    ];
    const baseConfig = {
      circuits: [
        {
          components: [
            { name: "exv", type: "IsenthalpicExpansionValve", opening: 0.5, orifice_kv: 2e-6 },
          ],
        },
      ],
    };
    const warnings = getSweepWarnings(target, nodes, baseConfig);
    expect(warnings).toEqual([]);
  });
});
