import { describe, expect, it } from "vitest";
import type { Edge, Node } from "@xyflow/react";
import {
  boundaryFixPairingPatches,
  enforceModelicaBoundaryEmit,
  findSecondaryLoopDofConflicts,
  hydrateBoundaryFixFlags,
  isBoundaryParamFixed,
  type BoundaryNodeData,
} from "./boundaryFix";
import { COMPONENT_BY_TYPE, fixedFlagKey } from "./componentMeta";
import { buildScenarioConfig, stripUiOnlyParams, type EntropykNodeData } from "./configBuilder";

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

function waterLoop(
  srcParams: Record<string, number | string | boolean>,
  sinkParams: Record<string, number | string | boolean>,
) {
  const nodes = [
    node("e", "FloodedEvaporator", "evap", { ua: 8000 }),
    node("s", "BrineSource", "src", srcParams),
    node("k", "BrineSink", "sink", sinkParams),
  ];
  const edges: Edge[] = [
    { id: "1", source: "s", target: "e", sourceHandle: "outlet", targetHandle: "secondary_inlet" },
    { id: "2", source: "e", target: "k", sourceHandle: "secondary_outlet", targetHandle: "inlet" },
  ];
  return { nodes, edges };
}

describe("Modelica MassFlowSource_T defaults", () => {
  it("meta defaultFixed P is false (Free P)", () => {
    const metaP = COMPONENT_BY_TYPE.BrineSource!.params.find((p) => p.key === "p_set_bar")!;
    expect(metaP.defaultFixed).toBe(false);
  });

  it("hydrate Free P when ṁ present without explicit fix_pressure", () => {
    const metaP = COMPONENT_BY_TYPE.BrineSource!.params.find((p) => p.key === "p_set_bar")!;
    const hydrated = hydrateBoundaryFixFlags("BrineSource", {
      p_set_bar: 3,
      t_set_c: 12,
      m_flow_kg_s: 0.55,
    });
    expect(hydrated[fixedFlagKey("p_set_bar")]).toBe(false);
    expect(isBoundaryParamFixed(hydrated, metaP)).toBe(false);
  });

  it("pairing: Fixed ṁ frees P on source", () => {
    const { nodes, edges } = waterLoop(
      { m_flow_kg_s: 0.55, [fixedFlagKey("p_set_bar")]: true },
      { p_back_bar: 3 },
    );
    const patches = boundaryFixPairingPatches("s", "m_flow_kg_s", true, nodes, edges);
    expect(patches.get("s")?.[fixedFlagKey("p_set_bar")]).toBe(false);
  });

  it("pairing: Fixed P frees ṁ (Boundary_pT)", () => {
    const { nodes, edges } = waterLoop({ m_flow_kg_s: 0.55 }, { p_back_bar: 3 });
    const patches = boundaryFixPairingPatches("s", "p_set_bar", true, nodes, edges);
    expect(patches.get("s")?.[fixedFlagKey("m_flow_kg_s")]).toBe(false);
  });

  it("detects Fixed P + Fixed ṁ on source", () => {
    const { nodes, edges } = waterLoop(
      {
        m_flow_kg_s: 0.55,
        [fixedFlagKey("m_flow_kg_s")]: true,
        [fixedFlagKey("p_set_bar")]: true,
      },
      { p_back_bar: 3, [fixedFlagKey("p_back_bar")]: true },
    );
    const conflicts = findSecondaryLoopDofConflicts(nodes, edges);
    expect(conflicts.some((c) => c.message.includes("MassFlowSource_T"))).toBe(true);
  });
});

describe("enforceModelicaBoundaryEmit", () => {
  it("MassFlowSource emit: Free P + Fixed ṁ", () => {
    const { nodes, edges } = waterLoop(
      {
        p_set_bar: 3,
        t_set_c: 12,
        m_flow_kg_s: 0.55,
        [fixedFlagKey("m_flow_kg_s")]: true,
        [fixedFlagKey("p_set_bar")]: true,
        [fixedFlagKey("t_set_c")]: true,
      },
      { p_back_bar: 3, [fixedFlagKey("p_back_bar")]: true },
    );
    const components = nodes.map((n) => ({
      type: n.data.type,
      name: n.data.name,
      ...stripUiOnlyParams(n.data.type, n.data.params),
    })) as Record<string, unknown>[];
    enforceModelicaBoundaryEmit(components, nodes, edges);
    const src = components.find((c) => c.name === "src")!;
    expect(src.fix_mass_flow).toBe(true);
    expect(src.fix_pressure).toBe(false);
  });

  it("isobaric + Fixed T_out: Free ṁ and Free P on source", () => {
    const { nodes, edges } = waterLoop(
      {
        p_set_bar: 3,
        t_set_c: 12,
        m_flow_kg_s: 0.55,
        [fixedFlagKey("m_flow_kg_s")]: true,
        [fixedFlagKey("p_set_bar")]: true,
        [fixedFlagKey("t_set_c")]: true,
      },
      {
        p_back_bar: 3,
        t_set_c: 7,
        [fixedFlagKey("p_back_bar")]: true,
        [fixedFlagKey("t_set_c")]: true,
      },
    );
    const rfNodes: Node<EntropykNodeData>[] = nodes.map((n) => ({
      ...n,
      data: {
        type: n.data.type,
        name: n.data.name,
        circuit: 0,
        rotation: 0,
        flipH: false,
        flipV: false,
        params: n.data.params,
      },
    }));
    const cfg = buildScenarioConfig(rfNodes, edges);
    const src = cfg.circuits[0].components.find((c) => c.name === "src")!;
    expect(src.fix_mass_flow).toBe(false);
    expect(src.fix_pressure).toBe(false);
  });
});
