"use client";
import { useEffect, useMemo, useState } from "react";
import ACSolver from "./ACSolver";
import GensetSolver from "./GensetSolver";
import AdvancedFacilitiesSolver from "./AdvancedFacilitiesSolver";
import "./solver-lab.css";

type Tab =
  | "lift"
  | "escalator"
  | "pipe"
  | "hydrant"
  | "plumbing"
  | "power"
  | "light"
  | "fire"
  | "ict"
  | "ac"
  | "genset"
  | "advanced"
  | "output"
  | "test";
type Pipe = {
  id: string;
  l: number;
  d: number;
  q: number;
  c: number;
  dz: number;
};
type Circuit = {
  id: string;
  name: string;
  w: number;
  pf: number;
  l: number;
  s: number;
  cb: number;
};
type Rack = {
  id: string;
  zone: string;
  outlet: number;
  poe: number;
  l: number;
  u: number;
};
const fmt = (v: number, d = 2) =>
  new Intl.NumberFormat("id-ID", { maximumFractionDigits: d }).format(
    Number.isFinite(v) ? v : 0,
  );
const gates = (ok: boolean) => (
  <b className={`slGate ${ok ? "pass" : "hold"}`}>{ok ? "PASS" : "HOLD"}</b>
);
const initialPipes: Pipe[] = [
  { id: "P-01", l: 42, d: 100, q: 18, c: 120, dz: 4 },
  { id: "P-02", l: 55, d: 80, q: 12, c: 120, dz: 8 },
  { id: "P-03", l: 38, d: 65, q: 7, c: 120, dz: 8 },
];
const initialCircuits: Circuit[] = [
  {
    id: "C-01",
    name: "Lighting tipikal",
    w: 9200,
    pf: 0.9,
    l: 48,
    s: 6,
    cb: 20,
  },
  {
    id: "C-02",
    name: "Socket outlet",
    w: 13800,
    pf: 0.9,
    l: 62,
    s: 10,
    cb: 32,
  },
  { id: "C-03", name: "Fire pump", w: 45000, pf: 0.85, l: 85, s: 35, cb: 100 },
];
const initialRacks: Rack[] = [
  { id: "IDF-01", zone: "Lantai Dasar", outlet: 72, poe: 24, l: 68, u: 27 },
  { id: "IDF-02", zone: "Lantai 2–5", outlet: 192, poe: 64, l: 82, u: 42 },
];

export default function SolverLab() {
  const [tab, setTab] = useState<Tab>("lift"),
    [project, setProject] = useState("Menara SJP Bandung – Proyek Pembanding");
  const [lift, setLift] = useState({
    population: 2400,
    peak: 12,
    capacity: 15,
    units: 4,
    rtt: 115,
    floors: 12,
    speed: 2.5,
    shaft: 6.5,
  });
  const [escalator, setEscalator] = useState({
    upFlow: 3200,
    downFlow: 2100,
    speed: 0.5,
    pitch: 0.4,
    occupancy: 1,
    utilization: 0.65,
    installed: 4,
    rise: 5.4,
    angle: 30,
  });
  const [pipes, setPipes] = useState(initialPipes),
    [head, setHead] = useState(55),
    [minHead, setMinHead] = useState(10);
  const [hydrant, setHydrant] = useState({
    outlets: 2,
    flowOutlet: 6.3,
    duration: 45,
    l: 120,
    d: 100,
    c: 120,
    dz: 32,
    residual: 45,
    eff: 0.7,
    availableTank: 80,
  });
  const [plumbing, setPlumbing] = useState({
    occupants: 1200,
    demand: 100,
    hours: 10,
    peak: 2.5,
    storage: 1.0,
    roofShare: 0.3,
    fixtureUnits: 680,
    pipeD: 100,
    head: 38,
    eff: 0.7,
  });
  const [circuits, setCircuits] = useState(initialCircuits),
    [volt, setVolt] = useState(400),
    [fault, setFault] = useState(25),
    [upstream, setUpstream] = useState(630);
  const [room, setRoom] = useState({
    x: 12,
    y: 8,
    h: 3.2,
    wp: 0.8,
    nx: 4,
    ny: 3,
    lm: 3600,
    w: 32,
    cu: 0.72,
    mf: 0.8,
    target: 350,
  });
  const [fire, setFire] = useState({
    density: 5,
    area: 140,
    k: 80,
    p: 1,
    heads: 12,
    l: 95,
    d: 100,
    c: 120,
    dz: 24,
    duration: 60,
  });
  const [cause, setCause] = useState<Record<string, boolean>>({
    "Smoke:Alarm": true,
    "Smoke:AHU stop": true,
    "MCP:Alarm": true,
    "Flow switch:Alarm": true,
    "Flow switch:Fire pump": true,
  });
  const [racks, setRacks] = useState(initialRacks),
    [reserve, setReserve] = useState(25);
  useEffect(() => {
    try {
      const x = JSON.parse(localStorage.getItem("mep-solvers-v4") || "null");
      if (x) {
        setProject(x.project);
        if (x.lift) setLift(x.lift);
        if (x.escalator) setEscalator(x.escalator);
        if (x.hydrant) setHydrant(x.hydrant);
        if (x.plumbing) setPlumbing(x.plumbing);
        setPipes(x.pipes);
        setCircuits(x.circuits);
        setRoom(x.room);
        setFire(x.fire);
        setCause(x.cause);
        setRacks(x.racks);
      }
    } catch {}
  }, []);
  useEffect(
    () =>
      localStorage.setItem(
        "mep-solvers-v4",
        JSON.stringify({
          project,
          lift,
          escalator,
          hydrant,
          plumbing,
          pipes,
          circuits,
          room,
          fire,
          cause,
          racks,
        }),
      ),
    [
      project,
      lift,
      escalator,
      hydrant,
      plumbing,
      pipes,
      circuits,
      room,
      fire,
      cause,
      racks,
    ],
  );
  const liftResult = useMemo(() => {
    const peakPeople = (lift.population * lift.peak) / 100,
      handling = (300 * lift.capacity * lift.units) / lift.rtt,
      ratio = (handling / peakPeople) * 100,
      interval = lift.rtt / lift.units,
      required = Math.ceil((peakPeople * lift.rtt) / (300 * lift.capacity)),
      travel = (lift.floors - 1) * 4,
      core = lift.units * lift.shaft;
    return {
      peakPeople,
      handling,
      ratio,
      interval,
      required,
      travel,
      core,
      ok: lift.units >= required && interval <= 35 && ratio >= 100,
    };
  }, [lift]);
  const escalatorResult = useMemo(() => {
    const capacity =
        ((3600 * escalator.speed) / escalator.pitch) *
        escalator.occupancy *
        escalator.utilization,
      up = Math.ceil(escalator.upFlow / capacity),
      down = Math.ceil(escalator.downFlow / capacity),
      required = up + down,
      length = escalator.rise / Math.sin((escalator.angle * Math.PI) / 180);
    return {
      capacity,
      up,
      down,
      required,
      length,
      ok:
        escalator.installed >= required &&
        escalator.angle >= 27 &&
        escalator.angle <= 35,
    };
  }, [escalator]);
  const pipeResult = useMemo(() => {
    let h = head;
    return pipes.map((p) => {
      const q = p.q / 1000,
        d = p.d / 1000,
        hf = (10.67 * p.l * q ** 1.852) / (p.c ** 1.852 * d ** 4.87),
        v = q / ((Math.PI * d * d) / 4);
      h -= hf + p.dz;
      return { ...p, hf, v, h, ok: h >= minHead && v >= 0.6 && v <= 3 };
    });
  }, [pipes, head, minHead]);
  const hydrantResult = useMemo(() => {
    const q = hydrant.outlets * hydrant.flowOutlet,
      qm = q / 1000,
      d = hydrant.d / 1000,
      hf = (10.67 * hydrant.l * qm ** 1.852) / (hydrant.c ** 1.852 * d ** 4.87),
      head = hydrant.dz + hydrant.residual / 9.81 + hf,
      tank = (q * 60 * hydrant.duration) / 1000,
      power = (1000 * 9.81 * qm * head) / hydrant.eff / 1000,
      v = qm / ((Math.PI * d * d) / 4);
    return {
      q,
      hf,
      head,
      tank,
      power,
      v,
      ok: hydrant.availableTank >= tank && v <= 3 && head > 0,
    };
  }, [hydrant]);
  const plumbingResult = useMemo(() => {
    const daily = (plumbing.occupants * plumbing.demand) / 1000,
      avg = daily / plumbing.hours,
      peak = avg * plumbing.peak,
      storage = daily * plumbing.storage,
      roof = storage * plumbing.roofShare,
      ground = storage - roof,
      q = peak / 3600,
      d = plumbing.pipeD / 1000,
      v = q / ((Math.PI * d * d) / 4),
      power = (1000 * 9.81 * q * plumbing.head) / plumbing.eff / 1000;
    return {
      daily,
      avg,
      peak,
      storage,
      roof,
      ground,
      v,
      power,
      ok: v >= 0.6 && v <= 2.5 && storage >= daily * 0.75,
    };
  }, [plumbing]);
  const powerResult = useMemo(
    () =>
      circuits.map((c) => {
        const a = c.w / (Math.sqrt(3) * volt * c.pf),
          vd = ((Math.sqrt(3) * a * c.l * 0.018) / c.s / volt) * 100,
          iz = c.s * 4.2,
          ik = Math.max(0.1, fault / (1 + c.l / (c.s * 18))),
          sel = upstream / c.cb;
        return {
          ...c,
          a,
          vd,
          iz,
          ik,
          sel,
          ok: a <= c.cb && c.cb <= iz && vd <= 4 && sel >= 1.6,
        };
      }),
    [circuits, volt, fault, upstream],
  );
  const lux = useMemo(() => {
    const fixtures = [] as { x: number; y: number }[];
    for (let x = 0; x < room.nx; x++)
      for (let y = 0; y < room.ny; y++)
        fixtures.push({
          x: ((x + 0.5) * room.x) / room.nx,
          y: ((y + 0.5) * room.y) / room.ny,
        });
    const points = [] as number[];
    for (let x = 0.5; x < room.x; x += 1)
      for (let y = 0.5; y < room.y; y += 1) {
        let e = 0;
        fixtures.forEach((l) => {
          const z = room.h - room.wp,
            r = Math.hypot(x - l.x, y - l.y, z);
          e += (room.lm / (Math.PI * r * r)) * (z / r) ** 3 * room.cu * room.mf;
        });
        points.push(e);
      }
    const avg = points.reduce((a, b) => a + b, 0) / points.length,
      min = Math.min(...points),
      max = Math.max(...points);
    return {
      points,
      avg,
      min,
      max,
      u: min / avg,
      lpd: (fixtures.length * room.w) / (room.x * room.y),
    };
  }, [room]);
  const fireResult = useMemo(() => {
    const q = Math.max(
        (fire.density * fire.area) / 60,
        (fire.heads * fire.k * Math.sqrt(fire.p)) / 60,
      ),
      qm = q / 1000,
      d = fire.d / 1000,
      hf = (10.67 * fire.l * qm ** 1.852) / (fire.c ** 1.852 * d ** 4.87),
      h = fire.dz + fire.p * 10.197 + hf;
    return {
      q,
      hf,
      h,
      tank: (q * 60 * fire.duration) / 1000,
      kw: (1000 * 9.81 * qm * h) / 0.7 / 1000,
    };
  }, [fire]);
  const ict = useMemo(
    () =>
      racks.map((r) => {
        const ports = Math.ceil((r.outlet * (1 + reserve / 100)) / 24) * 24,
          sw = Math.ceil(ports / 48),
          fiber = Math.max(12, Math.ceil(r.outlet / 24) * 4),
          pairs = Math.ceil(r.outlet * 1.2),
          used = sw + Math.ceil(r.poe / 24) + 6;
        return {
          ...r,
          ports,
          sw,
          fiber,
          pairs,
          used,
          ok: r.l <= 90 && used <= r.u,
        };
      }),
    [racks, reserve],
  );
  const boq = useMemo<
    { item: string; qty: number; unit: string; bab: string }[]
  >(
    () => [
      { item: "Lift penumpang", qty: lift.units, unit: "unit", bab: "I-A" },
      { item: "Eskalator", qty: escalator.installed, unit: "unit", bab: "I-B" },
      {
        item: "Pipa distribusi",
        qty: pipes.reduce((a, p) => a + p.l, 0),
        unit: "m",
        bab: "II",
      },
      {
        item: "Hydrant outlet",
        qty: hydrant.outlets,
        unit: "titik aktif",
        bab: "III",
      },
      {
        item: "Fire-water tank",
        qty: hydrantResult.tank,
        unit: "m³",
        bab: "III",
      },
      {
        item: "Domestic water storage",
        qty: plumbingResult.storage,
        unit: "m³",
        bab: "IV",
      },
      {
        item: "Kabel tenaga",
        qty: circuits.reduce((a, c) => a + c.l, 0),
        unit: "m",
        bab: "V",
      },
      { item: "MCB/MCCB", qty: circuits.length, unit: "unit", bab: "V" },
      { item: "Armatur LED", qty: room.nx * room.ny, unit: "unit", bab: "VI" },
      { item: "Sprinkler remote", qty: fire.heads, unit: "head", bab: "VII" },
      { item: "Pipa fire main", qty: fire.l, unit: "m", bab: "VII" },
      {
        item: "Data outlet",
        qty: racks.reduce((a, r) => a + r.outlet, 0),
        unit: "titik",
        bab: "VIII",
      },
      { item: "Rack MDF/IDF", qty: racks.length, unit: "unit", bab: "VIII" },
      {
        item: "Switch 48-port",
        qty: ict.reduce((a, r) => a + r.sw, 0),
        unit: "unit",
        bab: "VIII",
      },
    ],
    [
      lift,
      escalator,
      pipes,
      hydrant,
      hydrantResult,
      plumbingResult,
      circuits,
      room,
      fire,
      racks,
      ict,
    ],
  );
  const tests = [
    {
      n: "Lift handling 5 menit",
      a: (300 * 15 * 4) / 120,
      e: 150,
      t: 0.001,
      u: "orang",
    },
    {
      n: "Eskalator practical capacity",
      a: ((3600 * 0.5) / 0.4) * 0.65,
      e: 2925,
      t: 0.001,
      u: "orang/jam",
    },
    {
      n: "Hazen–Williams",
      a: (10.67 * 100 * 0.01 ** 1.852) / (120 ** 1.852 * 0.1 ** 4.87),
      e: 2.25,
      t: 0.08,
      u: "m",
    },
    {
      n: "Hydrant storage 12,6 L/s ×45 min",
      a: (12.6 * 60 * 45) / 1000,
      e: 34.02,
      t: 0.001,
      u: "m³",
    },
    {
      n: "Domestic demand 1.000×100 L",
      a: (1000 * 100) / 1000,
      e: 100,
      t: 0.001,
      u: "m³/hari",
    },
    {
      n: "Arus 3φ 40 kW",
      a: 40000 / (Math.sqrt(3) * 400 * 0.8),
      e: 72.17,
      t: 0.01,
      u: "A",
    },
    {
      n: "Voltage drop 3φ",
      a: ((Math.sqrt(3) * 50 * 50 * 0.018) / 10 / 400) * 100,
      e: 1.95,
      t: 0.02,
      u: "%",
    },
    {
      n: "Sprinkler K80 @1 bar",
      a: 80 * Math.sqrt(1),
      e: 80,
      t: 0.001,
      u: "L/min",
    },
    {
      n: "ICT channel gate",
      a: ict.every((r) => r.l <= 90) ? 1 : 0,
      e: 1,
      t: 0,
      u: "boolean",
    },
  ];
  const passTests = tests.filter(
    (t) => Math.abs(t.a - t.e) <= Math.max(0.0001, Math.abs(t.e) * t.t),
  ).length;
  const tabs: [Tab, string, string][] = [
    ["lift", "I-A", "Lift Traffic"],
    ["escalator", "I-B", "Eskalator"],
    ["pipe", "II", "Node–Pipe"],
    ["hydrant", "III", "Hidran"],
    ["plumbing", "IV", "Plumbing"],
    ["power", "V", "Panel & SLD"],
    ["light", "VI", "Point-by-Point"],
    ["fire", "VII", "Fire Hydraulic"],
    ["ict", "VIII", "ICT Schedule"],
    ["ac", "AC", "System Designer"],
    ["genset", "GEN", "Emergency Power"],
    ["advanced", "IX–XII", "Fasilitas Lanjutan"],
    ["output", "DED", "Gambar–Spek–BOQ"],
    ["test", "QA", "Benchmark"],
  ];
  const csv = () => {
    const a = document.createElement("a"),
      blob = new Blob(
        [
          [
            "Bab,Item,Kuantitas,Satuan",
            ...boq.map((x) => `${x.bab},${x.item},${x.qty},${x.unit}`),
          ].join("\n"),
        ],
        { type: "text/csv" },
      );
    a.href = URL.createObjectURL(blob);
    a.download = "MEP-BOQ-v4.csv";
    a.click();
  };
  return (
    <section className="solverLab">
      <div className="slHero">
        <div>
          <small>ENGINEERING SOLVER · VERSION 4.1</small>
          <h2>Solver Lab & DED Integration</h2>
          <p>
            Rumus deterministik, schedule, diagram, spesifikasi, BOQ dan
            regression test Bab I–VIII dalam satu sumber data.
          </p>
        </div>
        <label>
          Nama proyek
          <input value={project} onChange={(e) => setProject(e.target.value)} />
        </label>
      </div>
      <nav className="slTabs">
        {tabs.map((t) => (
          <button
            className={tab === t[0] ? "active" : ""}
            key={t[0]}
            onClick={() => setTab(t[0])}
          >
            <span>{t[1]}</span>
            <b>{t[2]}</b>
          </button>
        ))}
      </nav>
      {tab === "lift" && (
        <Page
          bab="BAB I-A"
          title="Lift traffic, jumlah unit & zoning"
          ok={liftResult.ok}
        >
          <ObjectInputs
            obj={lift}
            set={setLift}
            labels={{
              population: "Populasi",
              peak: "Peak 5 menit (%)",
              capacity: "Kapasitas (orang)",
              units: "Unit terpasang",
              rtt: "Round-trip time (s)",
              floors: "Jumlah lantai",
              speed: "Kecepatan (m/s)",
              shaft: "Luas shaft/unit",
            }}
          />
          <Kpis
            values={[
              "Peak demand",
              `${fmt(liftResult.peakPeople, 0)} orang`,
              "Handling",
              `${fmt(liftResult.handling, 0)} orang/5 min`,
              "Interval",
              `${fmt(liftResult.interval)} s`,
              "Unit minimum",
              `${liftResult.required} unit`,
            ]}
          />
          <Grid heads={["Parameter", "Hasil", "Kriteria", "Gate"]}>
            <tr>
              <td>Handling ratio</td>
              <td>{fmt(liftResult.ratio)}%</td>
              <td>≥100% peak demand</td>
              <td>{gates(liftResult.ratio >= 100)}</td>
            </tr>
            <tr>
              <td>Interval</td>
              <td>{fmt(liftResult.interval)} s</td>
              <td>≤35 s screening kantor</td>
              <td>{gates(liftResult.interval <= 35)}</td>
            </tr>
            <tr>
              <td>Jumlah lift</td>
              <td>{lift.units} unit</td>
              <td>Minimum {liftResult.required}</td>
              <td>{gates(lift.units >= liftResult.required)}</td>
            </tr>
            <tr>
              <td>Luas core</td>
              <td>{fmt(liftResult.core)} m²</td>
              <td>Koordinasi arsitektur</td>
              <td>{gates(liftResult.core > 0)}</td>
            </tr>
          </Grid>
          <Formula>
            HC₅=300×kapasitas×unit/RTT; interval=RTT/unit; kebutuhan
            unit=ceil(peak×RTT/(300×kapasitas)). Final memakai traffic
            simulation vendor dan zoning pengguna.
          </Formula>
          <Diagram
            labels={[
              "Populasi & peak",
              `Lobby dispatch\n${fmt(liftResult.peakPeople, 0)} orang`,
              ...Array.from(
                { length: lift.units },
                (_, i) =>
                  `Lift L-${i + 1}\n${lift.capacity} orang\n${lift.speed} m/s`,
              ),
            ]}
          />
        </Page>
      )}
      {tab === "escalator" && (
        <Page
          bab="BAB I-B"
          title="Kapasitas, jumlah & geometri eskalator"
          ok={escalatorResult.ok}
        >
          <ObjectInputs
            obj={escalator}
            set={setEscalator}
            labels={{
              upFlow: "Arus naik/jam",
              downFlow: "Arus turun/jam",
              speed: "Kecepatan m/s",
              pitch: "Step pitch m",
              occupancy: "Orang/step",
              utilization: "Faktor praktis",
              installed: "Unit terpasang",
              rise: "Rise m",
              angle: "Sudut derajat",
            }}
          />
          <Kpis
            values={[
              "Kapasitas/unit",
              `${fmt(escalatorResult.capacity, 0)} orang/jam`,
              "Naik minimum",
              `${escalatorResult.up} unit`,
              "Turun minimum",
              `${escalatorResult.down} unit`,
              "Total minimum",
              `${escalatorResult.required} unit`,
            ]}
          />
          <Grid heads={["Parameter", "Hasil", "Kriteria", "Gate"]}>
            <tr>
              <td>Jumlah terpasang</td>
              <td>{escalator.installed} unit</td>
              <td>≥{escalatorResult.required}</td>
              <td>{gates(escalator.installed >= escalatorResult.required)}</td>
            </tr>
            <tr>
              <td>Sudut</td>
              <td>{escalator.angle}°</td>
              <td>27°–35° screening</td>
              <td>{gates(escalator.angle >= 27 && escalator.angle <= 35)}</td>
            </tr>
            <tr>
              <td>Panjang miring</td>
              <td>{fmt(escalatorResult.length)} m</td>
              <td>Koordinasi void/support</td>
              <td>{gates(escalatorResult.length > escalator.rise)}</td>
            </tr>
          </Grid>
          <Formula>
            Kapasitas praktis=3600×v/pitch×occupancy×utilization; unit
            arah=ceil(peak flow/kapasitas praktis).
          </Formula>
          <Diagram
            labels={[
              "Arus pengunjung",
              `Naik\n${escalatorResult.up} unit`,
              `Landing\nRise ${escalator.rise} m`,
              `Turun\n${escalatorResult.down} unit`,
            ]}
          />
        </Page>
      )}
      {tab === "pipe" && (
        <Page
          bab="BAB II"
          title="Jaringan hidraulik node–pipe"
          ok={pipeResult.every((x) => x.ok)}
        >
          <Inputs
            items={[
              "Head sumber (m)",
              head,
              setHead,
              "Head minimum (m)",
              minHead,
              setMinHead,
            ]}
          />
          <Grid
            heads={[
              "Pipa",
              "L",
              "Ø",
              "Q",
              "C",
              "Δz",
              "hf",
              "v",
              "H node",
              "Gate",
            ]}
          >
            {pipeResult.map((p, i) => (
              <tr key={p.id}>
                <td>{p.id}</td>
                {(["l", "d", "q", "c", "dz"] as const).map((k) => (
                  <td key={k}>
                    <input
                      type="number"
                      value={p[k]}
                      onChange={(e) =>
                        setPipes(
                          pipes.map((x, j) =>
                            j === i ? { ...x, [k]: +e.target.value } : x,
                          ),
                        )
                      }
                    />
                  </td>
                ))}
                <td>{fmt(p.hf)}</td>
                <td>{fmt(p.v)}</td>
                <td>{fmt(p.h)}</td>
                <td>{gates(p.ok)}</td>
              </tr>
            ))}
          </Grid>
          <Add
            onClick={() =>
              setPipes([
                ...pipes,
                {
                  id: `P-${pipes.length + 1}`,
                  l: 20,
                  d: 65,
                  q: 5,
                  c: 120,
                  dz: 4,
                },
              ])
            }
          />
          <Formula>
            hf = 10,67 LQ^1,852/(C^1,852D^4,87); Hnode = Hsebelumnya − hf − Δz.
          </Formula>
          <Diagram
            labels={[
              "Sumber",
              ...pipeResult.map(
                (p) => `${p.id}\nØ${p.d} · ${fmt(p.q)} L/s\nH ${fmt(p.h)} m`,
              ),
            ]}
          />
        </Page>
      )}
      {tab === "hydrant" && (
        <Page
          bab="BAB III"
          title="Hidran, standpipe & fire-water supply"
          ok={hydrantResult.ok}
        >
          <ObjectInputs
            obj={hydrant}
            set={setHydrant}
            labels={{
              outlets: "Outlet simultan",
              flowOutlet: "Debit/outlet L/s",
              duration: "Durasi menit",
              l: "Panjang kritis m",
              d: "Ø main mm",
              c: "Hazen C",
              dz: "Elevasi m",
              residual: "Residual kPa",
              eff: "Efisiensi pompa",
              availableTank: "Tangki tersedia m³",
            }}
          />
          <Kpis
            values={[
              "Debit desain",
              `${fmt(hydrantResult.q)} L/s`,
              "Head pompa",
              `${fmt(hydrantResult.head)} m`,
              "Reservoir",
              `${fmt(hydrantResult.tank)} m³`,
              "Motor",
              `${fmt(hydrantResult.power)} kW`,
            ]}
          />
          <Grid heads={["Pemeriksaan", "Hasil", "Kriteria", "Gate"]}>
            <tr>
              <td>Kecepatan main</td>
              <td>{fmt(hydrantResult.v)} m/s</td>
              <td>≤3 m/s screening</td>
              <td>{gates(hydrantResult.v <= 3)}</td>
            </tr>
            <tr>
              <td>Friction loss</td>
              <td>{fmt(hydrantResult.hf)} m</td>
              <td>Masuk TDH</td>
              <td>{gates(hydrantResult.hf > 0)}</td>
            </tr>
            <tr>
              <td>Kapasitas tangki</td>
              <td>{hydrant.availableTank} m³</td>
              <td>≥{fmt(hydrantResult.tank)} m³</td>
              <td>{gates(hydrant.availableTank >= hydrantResult.tank)}</td>
            </tr>
          </Grid>
          <Formula>
            Q=outlet simultan×debit outlet; V=Q×durasi; TDH=Δz+Presidual/ρg+hf.
            Final mengikuti klasifikasi, zona tekanan, FDC, PRV, fire-pump curve
            dan AHJ.
          </Formula>
          <Diagram
            labels={[
              "Fire tank",
              `Fire pump\n${fmt(hydrantResult.q)} L/s @ ${fmt(hydrantResult.head)} m`,
              `Riser\nØ${hydrant.d} mm`,
              ...Array.from(
                { length: hydrant.outlets },
                (_, i) => `Hydrant H-${i + 1}\n${hydrant.flowOutlet} L/s`,
              ),
            ]}
          />
        </Page>
      )}
      {tab === "plumbing" && (
        <Page
          bab="BAB IV"
          title="Plumbing, water balance & storage"
          ok={plumbingResult.ok}
        >
          <ObjectInputs
            obj={plumbing}
            set={setPlumbing}
            labels={{
              occupants: "Penghuni",
              demand: "Kebutuhan L/org/hari",
              hours: "Jam operasi",
              peak: "Peak factor",
              storage: "Faktor tampungan",
              roofShare: "Porsi roof tank",
              fixtureUnits: "Total fixture unit",
              pipeD: "Ø main mm",
              head: "Head pompa m",
              eff: "Efisiensi pompa",
            }}
          />
          <Kpis
            values={[
              "Kebutuhan harian",
              `${fmt(plumbingResult.daily)} m³/hari`,
              "Debit puncak",
              `${fmt(plumbingResult.peak)} m³/jam`,
              "Ground tank",
              `${fmt(plumbingResult.ground)} m³`,
              "Roof tank",
              `${fmt(plumbingResult.roof)} m³`,
            ]}
          />
          <Grid heads={["Pemeriksaan", "Hasil", "Kriteria", "Gate"]}>
            <tr>
              <td>Kecepatan main</td>
              <td>{fmt(plumbingResult.v)} m/s</td>
              <td>0,6–2,5 m/s</td>
              <td>
                {gates(plumbingResult.v >= 0.6 && plumbingResult.v <= 2.5)}
              </td>
            </tr>
            <tr>
              <td>Storage total</td>
              <td>{fmt(plumbingResult.storage)} m³</td>
              <td>≥75% kebutuhan harian</td>
              <td>
                {gates(plumbingResult.storage >= plumbingResult.daily * 0.75)}
              </td>
            </tr>
            <tr>
              <td>Daya pompa</td>
              <td>{fmt(plumbingResult.power)} kW</td>
              <td>Positif</td>
              <td>{gates(plumbingResult.power > 0)}</td>
            </tr>
            <tr>
              <td>Fixture unit</td>
              <td>{fmt(plumbing.fixtureUnits, 0)} FU</td>
              <td>Schedule wajib tersedia</td>
              <td>{gates(plumbing.fixtureUnits > 0)}</td>
            </tr>
          </Grid>
          <Formula>
            Vharian=populasi×kebutuhan; Qpeak=(Vharian/jam operasi)×peak factor;
            P=ρgQH/η. Fixture-unit final harus dihitung per jenis alat plambing
            dan zona.
          </Formula>
          <Diagram
            labels={[
              "Sumber air",
              `Ground tank\n${fmt(plumbingResult.ground)} m³`,
              `Transfer pump\n${fmt(plumbingResult.power)} kW`,
              `Roof tank\n${fmt(plumbingResult.roof)} m³`,
              "Fixture zones",
            ]}
          />
        </Page>
      )}
      {tab === "power" && (
        <Page
          bab="BAB V"
          title="Panel schedule, SLD, short-circuit & selectivity"
          ok={powerResult.every((x) => x.ok)}
        >
          <Inputs
            items={[
              "Tegangan (V)",
              volt,
              setVolt,
              "Fault sumber (kA)",
              fault,
              setFault,
              "Breaker upstream (A)",
              upstream,
              setUpstream,
            ]}
          />
          <Grid
            heads={[
              "Circuit",
              "Beban",
              "W",
              "PF",
              "L",
              "mm²",
              "CB",
              "A",
              "VD%",
              "Ik",
              "Sel.",
              "Gate",
            ]}
          >
            {powerResult.map((c, i) => (
              <tr key={c.id}>
                <td>{c.id}</td>
                <td>
                  <input
                    value={c.name}
                    onChange={(e) =>
                      setCircuits(
                        circuits.map((x, j) =>
                          j === i ? { ...x, name: e.target.value } : x,
                        ),
                      )
                    }
                  />
                </td>
                {(["w", "pf", "l", "s", "cb"] as const).map((k) => (
                  <td key={k}>
                    <input
                      type="number"
                      value={c[k]}
                      onChange={(e) =>
                        setCircuits(
                          circuits.map((x, j) =>
                            j === i ? { ...x, [k]: +e.target.value } : x,
                          ),
                        )
                      }
                    />
                  </td>
                ))}
                <td>{fmt(c.a)}</td>
                <td>{fmt(c.vd)}</td>
                <td>{fmt(c.ik)}</td>
                <td>{fmt(c.sel)}</td>
                <td>{gates(c.ok)}</td>
              </tr>
            ))}
          </Grid>
          <Add
            onClick={() =>
              setCircuits([
                ...circuits,
                {
                  id: `C-${circuits.length + 1}`,
                  name: "Circuit baru",
                  w: 5000,
                  pf: 0.9,
                  l: 30,
                  s: 4,
                  cb: 16,
                },
              ])
            }
          />
          <Formula>
            I=P/(√3·V·PF); Ib≤In≤Iz; VD≤4%; rasio breaker
            upstream/downstream≥1,6. Selectivity final memakai kurva
            time-current vendor.
          </Formula>
          <Diagram
            labels={[
              `Sumber ${fault} kA`,
              `MDB ${upstream} A`,
              ...circuits.map((c) => `${c.id}\n${c.cb} A · ${c.s} mm²`),
            ]}
          />
        </Page>
      )}
      {tab === "light" && (
        <Page
          bab="BAB VI"
          title="Fotometri point-by-point"
          ok={lux.avg >= room.target && lux.u >= 0.4}
        >
          <ObjectInputs
            obj={room}
            set={setRoom}
            labels={{
              x: "Panjang",
              y: "Lebar",
              h: "Tinggi",
              wp: "Bidang kerja",
              nx: "Armatur X",
              ny: "Armatur Y",
              lm: "Lumen",
              w: "Watt",
              cu: "CU",
              mf: "MF",
              target: "Target lux",
            }}
          />
          <Kpis
            values={[
              "Eavg",
              `${fmt(lux.avg, 0)} lux`,
              `Emin`,
              `${fmt(lux.min, 0)} lux`,
              `Uniformity`,
              fmt(lux.u),
              "LPD",
              `${fmt(lux.lpd)} W/m²`,
            ]}
          />
          <div
            className="slHeat"
            style={{
              gridTemplateColumns: `repeat(${Math.max(1, Math.round(room.x))},1fr)`,
            }}
          >
            {lux.points.map((p, i) => (
              <span
                key={i}
                style={{
                  background: `hsl(${Math.min(120, (p / room.target) * 100)},70%,75%)`,
                }}
              >
                {fmt(p, 0)}
              </span>
            ))}
          </div>
          <Formula>
            Epoint=Σ[Φ/(πr²)·cos³θ·CU·MF]. Final wajib menggunakan file IES/LDT
            armatur terpilih.
          </Formula>
        </Page>
      )}
      {tab === "fire" && (
        <Page
          bab="BAB VII"
          title="Fire hydraulic & cause-and-effect"
          ok={
            fireResult.h > 0 && Object.values(cause).filter(Boolean).length >= 5
          }
        >
          <ObjectInputs
            obj={fire}
            set={setFire}
            labels={{
              density: "Density",
              area: "Remote area",
              k: "K-factor",
              p: "P bar",
              heads: "Head remote",
              l: "Panjang",
              d: "Ø main",
              c: "Hazen C",
              dz: "Elevasi",
              duration: "Durasi",
            }}
          />
          <Kpis
            values={[
              "Debit",
              `${fmt(fireResult.q)} L/s`,
              "Head",
              `${fmt(fireResult.h)} m`,
              "Tangki",
              `${fmt(fireResult.tank)} m³`,
              "Motor",
              `${fmt(fireResult.kw)} kW`,
            ]}
          />
          <h3>Cause-and-effect matrix</h3>
          <div className="slCause">
            {["Smoke", "Heat", "MCP", "Flow switch", "Fire pump"].map((a) => (
              <article key={a}>
                <b>{a}</b>
                {[
                  "Alarm",
                  "AHU stop",
                  "Lift recall",
                  "Door release",
                  "Fire pump",
                ].map((b) => {
                  const k = `${a}:${b}`;
                  return (
                    <label key={b}>
                      <input
                        type="checkbox"
                        checked={!!cause[k]}
                        onChange={() => setCause({ ...cause, [k]: !cause[k] })}
                      />
                      {b}
                    </label>
                  );
                })}
              </article>
            ))}
          </div>
          <Formula>
            Q=max(density×remote area, ΣK√P); H=elevasi+Presidual+hf. Matriks
            wajib sesuai fire strategy dan persetujuan AHJ.
          </Formula>
        </Page>
      )}
      {tab === "ict" && (
        <Page
          bab="BAB VIII"
          title="Outlet, rack & backbone schedule"
          ok={ict.every((x) => x.ok)}
        >
          <Inputs items={["Cadangan port (%)", reserve, setReserve]} />
          <Grid
            heads={[
              "IDF",
              "Zona",
              "Outlet",
              "PoE",
              "Channel",
              "Rack U",
              "Port",
              "Switch",
              "Fiber",
              "Pair",
              "Used U",
              "Gate",
            ]}
          >
            {ict.map((r, i) => (
              <tr key={r.id}>
                <td>{r.id}</td>
                <td>
                  <input
                    value={r.zone}
                    onChange={(e) =>
                      setRacks(
                        racks.map((x, j) =>
                          j === i ? { ...x, zone: e.target.value } : x,
                        ),
                      )
                    }
                  />
                </td>
                {(["outlet", "poe", "l", "u"] as const).map((k) => (
                  <td key={k}>
                    <input
                      type="number"
                      value={r[k]}
                      onChange={(e) =>
                        setRacks(
                          racks.map((x, j) =>
                            j === i ? { ...x, [k]: +e.target.value } : x,
                          ),
                        )
                      }
                    />
                  </td>
                ))}
                <td>{r.ports}</td>
                <td>{r.sw}</td>
                <td>{r.fiber}</td>
                <td>{r.pairs}</td>
                <td>{r.used}</td>
                <td>{gates(r.ok)}</td>
              </tr>
            ))}
          </Grid>
          <Add
            onClick={() =>
              setRacks([
                ...racks,
                {
                  id: `IDF-${racks.length + 1}`,
                  zone: "Zona baru",
                  outlet: 48,
                  poe: 12,
                  l: 75,
                  u: 27,
                },
              ])
            }
          />
          <Formula>
            Port dibulatkan kelipatan 24; switch 48-port; channel≤90 m; rack
            harus memuat perangkat dan manajemen kabel.
          </Formula>
          <Diagram
            labels={[
              "MDF/Core",
              ...ict.map(
                (r) => `${r.id}\n${r.fiber}C fiber\n${r.outlet} outlet`,
              ),
            ]}
          />
        </Page>
      )}
      {tab === "ac" && <ACSolver />}
      {tab === "genset" && <GensetSolver />}
      {tab === "advanced" && <AdvancedFacilitiesSolver />}
      {tab === "output" && (
        <Page
          bab="DED"
          title="Generator gambar, spesifikasi & BOQ terhubung"
          ok={
            liftResult.ok &&
            escalatorResult.ok &&
            pipeResult.every((x) => x.ok) &&
            hydrantResult.ok &&
            plumbingResult.ok &&
            powerResult.every((x) => x.ok) &&
            ict.every((x) => x.ok)
          }
        >
          <div className="slDocs">
            <article>
              <h3>Drawing register</h3>
              {[
                ["MEP-I-LFT-01", "Lift traffic & zoning"],
                ["MEP-I-ESC-01", "Escalator capacity & geometry"],
                ["MEP-II-SCH-01", "Node–pipe"],
                ["MEP-III-HYD-01", "Hydrant riser & fire pump"],
                ["MEP-IV-PLB-01", "Plumbing water balance & riser"],
                ["MEP-V-SLD-01", "Single-line"],
                ["MEP-VI-LTG-01", "Point-by-point"],
                ["MEP-VII-FIR-01", "Hydraulic & C/E"],
                ["MEP-VIII-ICT-01", "Backbone"],
              ].map((x) => (
                <p key={x[0]}>
                  <b>{x[0]}</b>
                  <span>{x[1]}</span>
                </p>
              ))}
            </article>
            <article>
              <h3>Spesifikasi terhubung</h3>
              <ul>
                <li>
                  Transportasi vertikal: traffic, unit, speed, shaft, pit,
                  support dan interface.
                </li>
                <li>Pipa: debit, kecepatan, kehilangan tekan, tekanan node.</li>
                <li>
                  Hidran: debit simultan, residual pressure, TDH, reservoir dan
                  pump curve.
                </li>
                <li>
                  Plumbing: fixture unit, water balance, storage, zoning dan
                  pump duty.
                </li>
                <li>
                  Elektrikal: ampacity, voltage drop, fault duty dan kurva
                  proteksi.
                </li>
                <li>Pencahayaan: IES/LDT, lumen, daya, CCT/CRI dan kontrol.</li>
                <li>
                  Fire: debit/head, listed equipment dan approved cause-effect.
                </li>
                <li>
                  ICT: channel, port reserve, rack, backbone dan test
                  certification.
                </li>
              </ul>
            </article>
          </div>
          <Grid heads={["Bab", "Item", "Kuantitas", "Satuan"]}>
            {boq.map((x) => (
              <tr key={x.item}>
                <td>{x.bab}</td>
                <td>{x.item}</td>
                <td>{fmt(x.qty)}</td>
                <td>{x.unit}</td>
              </tr>
            ))}
          </Grid>
          <div className="slActions">
            <button onClick={csv}>Unduh BOQ CSV</button>
            <button onClick={() => window.print()}>
              Cetak calculation package
            </button>
          </div>
          <p className="slNote">
            Final BOQ hanya dapat dirilis bila drawing, spesifikasi, model dan
            approval berada pada revisi yang sama.
          </p>
        </Page>
      )}
      {tab === "test" && (
        <Page
          bab="QA"
          title="Benchmark & regression test proyek pembanding"
          ok={passTests === tests.length}
        >
          <Kpis
            values={[
              "Test lulus",
              `${passTests}/${tests.length}`,
              "Coverage",
              "Bab I–VIII",
              "Compatibility",
              "v3 → v4.1",
              "Release",
              passTests === tests.length ? "PASS" : "HOLD",
            ]}
          />
          <Grid heads={["Test", "Expected", "Actual", "Tolerance", "Status"]}>
            {tests.map((t) => {
              const ok =
                Math.abs(t.a - t.e) <= Math.max(0.0001, Math.abs(t.e) * t.t);
              return (
                <tr key={t.n}>
                  <td>{t.n}</td>
                  <td>
                    {fmt(t.e)} {t.u}
                  </td>
                  <td>
                    {fmt(t.a)} {t.u}
                  </td>
                  <td>{fmt(t.t * 100)}%</td>
                  <td>{gates(ok)}</td>
                </tr>
              );
            })}
          </Grid>
          <p className="slNote">
            <b>{project}</b> adalah dataset contoh yang dapat diedit. Benchmark
            proyek nyata harus memakai calculation sheet yang telah disetujui
            sebagai expected result.
          </p>
        </Page>
      )}
    </section>
  );
}

function Page({
  bab,
  title,
  ok,
  children,
}: {
  bab: string;
  title: string;
  ok: boolean;
  children: React.ReactNode;
}) {
  return (
    <div className="slPage">
      <header>
        <div>
          <small>{bab} · CALCULATION ENGINE</small>
          <h2>{title}</h2>
        </div>
        {gates(ok)}
      </header>
      {children}
    </div>
  );
}
function Grid({
  heads,
  children,
}: {
  heads: string[];
  children: React.ReactNode;
}) {
  return (
    <div className="slGrid">
      <table>
        <thead>
          <tr>
            {heads.map((x) => (
              <th key={x}>{x}</th>
            ))}
          </tr>
        </thead>
        <tbody>{children}</tbody>
      </table>
    </div>
  );
}
function Inputs({
  items,
}: {
  items: (string | number | ((v: number) => void))[];
}) {
  const a = [];
  for (let i = 0; i < items.length; i += 3)
    a.push(
      <label key={String(items[i])}>
        {String(items[i])}
        <input
          type="number"
          value={items[i + 1] as number}
          onChange={(e) =>
            (items[i + 2] as (v: number) => void)(+e.target.value)
          }
        />
      </label>,
    );
  return <div className="slInputs">{a}</div>;
}
function ObjectInputs<T extends Record<string, number>>({
  obj,
  set,
  labels,
}: {
  obj: T;
  set: (x: T) => void;
  labels: Record<keyof T, string>;
}) {
  return (
    <div className="slInputs">
      {Object.keys(labels).map((k) => (
        <label key={k}>
          {labels[k as keyof T]}
          <input
            type="number"
            step="any"
            value={obj[k]}
            onChange={(e) => set({ ...obj, [k]: +e.target.value })}
          />
        </label>
      ))}
    </div>
  );
}
function Kpis({ values }: { values: string[] }) {
  const a = [];
  for (let i = 0; i < values.length; i += 2)
    a.push(
      <article key={values[i]}>
        <small>{values[i]}</small>
        <b>{values[i + 1]}</b>
      </article>,
    );
  return <div className="slKpi">{a}</div>;
}
function Formula({ children }: { children: React.ReactNode }) {
  return (
    <p className="slFormula">
      <b>Rumus & batas penggunaan</b>
      <span>{children}</span>
    </p>
  );
}
function Diagram({ labels }: { labels: string[] }) {
  return (
    <div className="slDiagram">
      {labels.map((x, i) => (
        <article key={`${x}-${i}`}>
          <b>{String(i + 1).padStart(2, "0")}</b>
          {x.split("\n").map((y) => (
            <span key={y}>{y}</span>
          ))}
        </article>
      ))}
    </div>
  );
}
function Add({ onClick }: { onClick: () => void }) {
  return (
    <button className="slAdd" onClick={onClick}>
      + Tambah baris
    </button>
  );
}
