import { useEffect, useRef, useState, useMemo } from 'react';
import { PART_DEFS, STANDARD_PIPE_SIZES, snapPipeSize } from '../parts/catalog';
import { useBuilder } from '../store/builderStore';
import { useSimulation, computeReservoirWaterStats } from '../simulation/flowSimulator';
import { computeRoomMetrics } from '../environment/roomMetrics';
import type { PlacedPart, Vec3 } from '../types';
import { ColorSwatchRow } from './ColorPopover';

const rad2deg = (r: number) => Math.round((r * 180) / Math.PI);
const deg2rad = (d: number) => (d * Math.PI) / 180;

const ELECTRICAL_PARTS = new Set<string>([
  'pump',
  'airCompressor',
  'waterChiller',
  'growLight',
  'inlineFan',
  'circulationFan',
]);

function formatDurationHelper(mins: number) {
  if (mins < 60) return `${mins}m`;
  const hrs = Math.floor(mins / 60);
  const remaining = mins % 60;
  return remaining > 0 ? `${hrs}h ${remaining}m` : `${hrs}h`;
}

/**
 * PropertiesPanel — right sidebar inspector.
 * One part selected: full inspector (pump quick-settings first for pumps).
 * Multiple parts: compact group panel (counts, color, shared actions).
 */
export function PropertiesPanel() {
  const selectedIds = useBuilder((s) => s.selectedIds);

  return (
    <aside className="flex w-64 shrink-0 flex-col border-l border-zinc-800 bg-zinc-950/80 backdrop-blur">
      <div className="border-b border-zinc-800 px-4 py-3">
        <h2 className="text-xs font-semibold tracking-widest text-zinc-400 uppercase">
          Inspector
          {selectedIds.length > 1 && (
            <span className="ml-2 rounded-full bg-sky-500/15 px-2 py-0.5 text-[10px] font-bold text-sky-300">
              {selectedIds.length} selected
            </span>
          )}
        </h2>
      </div>
      {selectedIds.length === 0 ? (
        <EmptyState />
      ) : selectedIds.length === 1 ? (
        <PartInspector partId={selectedIds[0]} />
      ) : (
        <MultiInspector ids={selectedIds} />
      )}
    </aside>
  );
}

function EmptyState() {
  const growthStage = useBuilder((s) => s.growthStage);
  const setGrowthStage = useBuilder((s) => s.setGrowthStage);
  return (
    <div className="px-4 py-6 text-xs leading-relaxed text-zinc-600 space-y-4">
      {/* Global Growth Stage selector */}
      <div className="rounded-lg border border-zinc-800 bg-zinc-900/30 p-3">
        <h3 className="mb-1.5 text-[10px] font-bold tracking-wider text-zinc-500 uppercase">Crop Growth Stage</h3>
        <select
          value={growthStage}
          onChange={(e) => setGrowthStage(e.target.value as any)}
          className="w-full rounded-md border border-zinc-800 bg-zinc-900 px-2 py-1.5 text-xs text-zinc-200 outline-none focus:border-sky-600 cursor-pointer"
        >
          <option value="seedling">Seedling</option>
          <option value="vegetative">Vegetative</option>
          <option value="flowering">Flowering</option>
        </select>
      </div>

      <div>
        Select a part to edit its properties.
        <ul className="mt-4 space-y-1.5 text-[11px] text-zinc-700">
        <li><Kbd>Click</Kbd> select · drag to move</li>
        <li><Kbd>⇧Click</Kbd> add/remove from selection</li>
        <li><Kbd>⌘Click</Kbd> change color</li>
        <li>Drag <span className="text-sky-600">connector dots</span> to snap pipes</li>
        <li><Kbd>⇧Drag</Kbd> move vertically (3D view)</li>
        <li><Kbd>⌥Drag</Kbd> clone while dragging</li>
        <li><Kbd>Space</Kbd> play / pause water</li>
        <li><Kbd>R</Kbd> rotate 90° &nbsp; <Kbd>Q</Kbd>/<Kbd>E</Kbd> lower / raise</li>
        <li><Kbd>D</Kbd> duplicate &nbsp; <Kbd>⌫</Kbd> delete</li>
        <li><Kbd>⌘Z</Kbd> undo &nbsp; <Kbd>⇧⌘Z</Kbd> redo</li>
        <li><Kbd>Esc</Kbd> deselect / cancel</li>
      </ul>
      </div>
    </div>
  );
}

function Kbd({ children }: { children: React.ReactNode }) {
  return (
    <kbd className="rounded border border-zinc-700 bg-zinc-800 px-1.5 py-0.5 font-mono text-[10px] text-zinc-300">
      {children}
    </kbd>
  );
}

// ---------------------------------------------------------------------------
// Multi-selection panel
// ---------------------------------------------------------------------------

function MultiInspector({ ids }: { ids: string[] }) {
  const parts = useBuilder((s) => s.parts);
  const removeParts = useBuilder((s) => s.removeParts);
  const duplicateParts = useBuilder((s) => s.duplicateParts);

  const counts = new Map<string, number>();
  for (const id of ids) {
    const part = parts[id];
    if (!part) continue;
    const label = PART_DEFS[part.type].label;
    counts.set(label, (counts.get(label) ?? 0) + 1);
  }

  return (
    <div className="flex-1 space-y-3 overflow-y-auto px-3.5 py-2.5">
      <Section title="Selection">
        <ul className="space-y-1">
          {[...counts.entries()].map(([label, n]) => (
            <li key={label} className="flex justify-between text-xs text-zinc-300">
              <span>{label}</span>
              <span className="font-mono text-zinc-500">× {n}</span>
            </li>
          ))}
        </ul>
        <p className="mt-2 text-[10px] leading-relaxed text-zinc-600">
          Drag any selected part (or the gizmo) to move the whole group.
        </p>
      </Section>

      <Section title="Color">
        <ColorSwatchRow ids={ids} />
      </Section>

      <div className="flex gap-2 pt-1">
        <button
          onClick={() => duplicateParts(ids)}
          title="Duplicate the whole selection (D)"
          className="flex-1 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-1 text-[11px] font-medium text-zinc-200 hover:bg-zinc-700"
        >
          Duplicate
        </button>
        <button
          onClick={() => removeParts(ids)}
          title="Delete every selected part (⌫)"
          className="flex-1 rounded-md border border-rose-900 bg-rose-950 px-2 py-1 text-[11px] font-medium text-rose-300 hover:bg-rose-900"
        >
          Delete
        </button>
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Pump quick-settings card
// ---------------------------------------------------------------------------

/** Pump param keys surfaced in the quick card (hidden from the generic list). */
const PUMP_CARD_KEYS = new Set(['gph', 'maxHeadFt']);
const AIR_COMPRESSOR_CARD_KEYS = new Set(['gph', 'frequency']);
const RESERVOIR_CARD_KEYS = new Set(['ph', 'ec']);

function PumpCard({ part }: { part: PlacedPart }) {
  const setParam = useBuilder((s) => s.setParam);
  const sim = useSimulation();
  const cardRef = useRef<HTMLDivElement>(null);
  const [flash, setFlash] = useState(false);

  // Double-clicking a pump in the scene scrolls to + flashes this card.
  useEffect(() => {
    const onFlash = (e: Event) => {
      if ((e as CustomEvent<string>).detail !== part.id) return;
      cardRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      setFlash(true);
      setTimeout(() => setFlash(false), 1000);
    };
    window.addEventListener('hydro:flash-pump', onFlash);
    return () => window.removeEventListener('hydro:flash-pump', onFlash);
  }, [part.id]);

  const def = PART_DEFS.pump;
  const on = (part.params.on ?? 1) > 0;
  const result = sim.pumps.find((p) => p.pumpId === part.id);
  const gph = part.params.gph ?? def.defaults.gph;
  const maxHead = part.params.maxHeadFt ?? def.defaults.maxHeadFt;

  return (
    <div
      ref={cardRef}
      className={`rounded-lg border p-3 transition-all duration-300 ${
        flash
          ? 'border-sky-400 bg-sky-950/60 ring-2 ring-sky-400/60'
          : 'border-sky-900/70 bg-sky-950/30'
      }`}
    >
      <div className="mb-2 flex items-center justify-between">
        <span className="text-[10px] font-bold tracking-wider text-sky-400 uppercase">Pump</span>
        {/* Big power toggle */}
        <button
          onClick={() => setParam(part.id, 'on', on ? 0 : 1)}
          title={on ? 'Turn pump off' : 'Turn pump on'}
          className={`relative h-6 w-12 rounded-full transition-colors ${
            on ? 'bg-emerald-500' : 'bg-zinc-700'
          }`}
        >
          <span
            className={`absolute top-0.5 grid h-5 w-5 place-items-center rounded-full bg-white text-[8px] font-black text-zinc-700 shadow transition-all ${
              on ? 'left-6' : 'left-0.5'
            }`}
          >
            {on ? 'I' : 'O'}
          </span>
        </button>
      </div>

      {/* Live delivered flow */}
      <div className="mb-2 rounded-md bg-zinc-900/70 px-2.5 py-1.5">
        <div className="text-lg font-bold tabular-nums text-sky-300">
          {result && on ? result.gph.toFixed(0) : '0'}
          <span className="ml-1 text-[10px] font-medium text-zinc-500">GPH delivered</span>
        </div>
        <div className="text-[10px] text-zinc-500 tabular-nums">
          {result
            ? `head ${result.headFt.toFixed(1)} / ${result.maxHeadFt} ft`
            : 'not connected to a reservoir'}
        </div>
      </div>

      <QuickSlider
        label="Rated flow"
        unit="GPH"
        value={gph}
        meta={def.params.gph}
        onChange={(v) => setParam(part.id, 'gph', v)}
      />
      <QuickSlider
        label="Max head"
        unit="ft"
        value={maxHead}
        meta={def.params.maxHeadFt}
        onChange={(v) => setParam(part.id, 'maxHeadFt', v)}
      />
    </div>
  );
}

function AirCompressorCard({ part }: { part: PlacedPart }) {
  const setParam = useBuilder((s) => s.setParam);
  const parts = useBuilder((s) => s.parts);
  const cardRef = useRef<HTMLDivElement>(null);
  const [flash, setFlash] = useState(false);

  // Double-clicking an air compressor in the scene scrolls to + flashes this card.
  useEffect(() => {
    const onFlash = (e: Event) => {
      if ((e as CustomEvent<string>).detail !== part.id) return;
      cardRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      setFlash(true);
      setTimeout(() => setFlash(false), 1000);
    };
    window.addEventListener('hydro:flash-airCompressor', onFlash);
    return () => window.removeEventListener('hydro:flash-airCompressor', onFlash);
  }, [part.id]);

  const def = PART_DEFS.airCompressor;
  const on = (part.params.on ?? 1) > 0;
  const gph = part.params.gph ?? def.defaults.gph;
  const frequency = part.params.frequency ?? def.defaults.frequency;

  // Find nearest reservoir
  const reservoir = useMemo(() => {
    let nearest: PlacedPart | null = null;
    let minDist = Infinity;
    const pos = part.position;
    for (const p of Object.values(parts)) {
      if (p.type === 'reservoir') {
        const d = Math.hypot(p.position[0] - pos[0], p.position[1] - pos[1], p.position[2] - pos[2]);
        if (d < minDist) {
          minDist = d;
          nearest = p;
        }
      }
    }
    return nearest;
  }, [parts, part.position]);

  // Compute stats if we have a reservoir
  const stats = useMemo(() => {
    if (!reservoir) return null;
    
    // Find all air compressors targeting this reservoir
    let totalAerationGph = 0;
    for (const p of Object.values(parts)) {
      if (p.type === 'airCompressor' && (p.params.on ?? 1) > 0) {
        // Find nearest reservoir for this compressor
        let compNearest: PlacedPart | null = null;
        let cMinDist = Infinity;
        for (const res of Object.values(parts)) {
          if (res.type === 'reservoir') {
            const d = Math.hypot(res.position[0] - p.position[0], res.position[1] - p.position[1], res.position[2] - p.position[2]);
            if (d < cMinDist) {
              cMinDist = d;
              compNearest = res;
            }
          }
        }
        if (compNearest && compNearest.id === reservoir.id) {
          totalAerationGph += p.params.gph ?? 120;
        }
      }
    }

    const w = reservoir.params.width ?? 2;
    const d = reservoir.params.depth ?? 1.4;
    const h = reservoir.params.height ?? 1;
    const level = reservoir.params.level ?? 70;
    const volGal = w * d * h * (level / 100) * 7.48;

    // TDO Calculation
    let tdo = 5.5;
    if (totalAerationGph > 0 && volGal > 0) {
      const ratio = totalAerationGph / volGal;
      tdo = 5.5 + 3.7 * (1 - Math.exp(-ratio * 2.0));
    }

    return {
      volGal,
      totalAerationGph,
      tdo,
    };
  }, [parts, reservoir]);

  return (
    <div
      ref={cardRef}
      className={`rounded-lg border p-3 transition-all duration-300 ${
        flash
          ? 'border-sky-400 bg-sky-950/60 ring-2 ring-sky-400/60'
          : 'border-sky-900/70 bg-sky-950/30'
      }`}
    >
      <div className="mb-2 flex items-center justify-between">
        <span className="text-[10px] font-bold tracking-wider text-sky-400 uppercase">Air Compressor</span>
        <button
          onClick={() => setParam(part.id, 'on', on ? 0 : 1)}
          title={on ? 'Turn compressor off' : 'Turn compressor on'}
          className={`relative h-6 w-12 rounded-full transition-colors ${
            on ? 'bg-emerald-500' : 'bg-zinc-700'
          }`}
        >
          <span
            className={`absolute top-0.5 grid h-5 w-5 place-items-center rounded-full bg-white text-[8px] font-black text-zinc-700 shadow transition-all ${
              on ? 'left-6' : 'left-0.5'
            }`}
          >
            {on ? 'I' : 'O'}
          </span>
        </button>
      </div>

      {/* Target and Live Dissolved Oxygen */}
      <div className="mb-3 rounded-md bg-zinc-900/70 px-2.5 py-1.5 space-y-1">
        <div className="text-[10px] font-semibold text-zinc-400">Target Reservoir:</div>
        {reservoir ? (
          <div>
            <div className="flex justify-between items-center text-xs text-zinc-200">
              <span className="font-medium">{reservoir.label || `Reservoir #${reservoir.id}`}</span>
              <span className="text-[10px] font-mono text-zinc-500">
                {Math.hypot(reservoir.position[0] - part.position[0], reservoir.position[2] - part.position[2]).toFixed(1)} ft away
              </span>
            </div>
            {stats && (
              <div className="mt-1.5 pt-1.5 border-t border-zinc-800/60 space-y-1.5">
                <div className="flex justify-between items-center">
                  <span className="text-[10px] text-zinc-500">Dissolved O₂:</span>
                  <span
                    className={`rounded px-1.5 py-0.5 text-[10px] font-bold ${
                      stats.tdo >= 8.0
                        ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20'
                        : stats.tdo >= 7.0
                        ? 'bg-sky-500/10 text-sky-400 border border-sky-500/20'
                        : 'bg-amber-500/10 text-amber-400 border border-amber-500/20'
                    }`}
                  >
                    {stats.tdo.toFixed(1)} mg/L
                  </span>
                </div>
                <div className="flex justify-between text-[9px] text-zinc-500">
                  <span>Tank Volume:</span>
                  <span className="font-mono text-zinc-300">{stats.volGal.toFixed(1)} Gal</span>
                </div>
                <div className="flex justify-between text-[9px] text-zinc-500">
                  <span>Total Aeration:</span>
                  <span className="font-mono text-zinc-300">{stats.totalAerationGph} GPH</span>
                </div>
              </div>
            )}
          </div>
        ) : (
          <div className="text-xs text-amber-400/80 font-medium">No reservoir in scene!</div>
        )}
      </div>

      <QuickSlider
        label="Air Flow"
        unit="GPH"
        value={gph}
        meta={def.params.gph}
        onChange={(v) => setParam(part.id, 'gph', v)}
      />
      <QuickSlider
        label="Bubble Frequency"
        unit="Hz"
        value={frequency}
        meta={def.params.frequency}
        onChange={(v) => setParam(part.id, 'frequency', v)}
      />
    </div>
  );
}

function GrowTentCard({ part }: { part: PlacedPart }) {
  const parts = useBuilder((s) => s.parts);
  const metrics = useMemo(() => {
    const allMetrics = computeRoomMetrics(parts);
    return allMetrics.find((m) => m.roomId === part.id);
  }, [parts, part.id]);

  if (!metrics) return null;

  return (
    <div className="rounded-lg border border-sky-900/50 bg-sky-950/20 p-3 space-y-3">
      <div className="flex items-center justify-between">
        <span className="text-[10px] font-bold tracking-wider text-sky-400 uppercase">Room Simulation</span>
        <span className="font-mono text-xs text-sky-300">{metrics.volumeFt3.toFixed(0)} ft³</span>
      </div>

      <div className="grid grid-cols-3 gap-2">
        {/* VPD */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">VPD</div>
          <div className="mt-0.5 text-xs font-bold text-emerald-400">
            {metrics.roomVPD.toFixed(2)}
            <span className="text-[8px] font-normal text-zinc-500 ml-0.5">kPa</span>
          </div>
        </div>

        {/* DLI */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">DLI</div>
          <div className="mt-0.5 text-xs font-bold text-amber-400">
            {metrics.roomDLI.toFixed(1)}
          </div>
        </div>

        {/* CO2 */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">CO₂</div>
          <div className="mt-0.5 text-xs font-bold text-sky-400">
            {metrics.roomCo2}
            <span className="text-[8px] font-normal text-zinc-500 ml-0.5">PPM</span>
          </div>
        </div>
      </div>

      <div className="text-[10px] text-zinc-400 space-y-1 pt-1.5 border-t border-zinc-800/60 font-mono">
        <div className="flex justify-between">
          <span>Air Exchange:</span>
          <span className="text-zinc-200">{metrics.airChangesPerMinute.toFixed(2)} /min</span>
        </div>
        <div className="flex justify-between">
          <span>Fresh Air:</span>
          <span className="text-zinc-200">{metrics.netFreshAirCfm.toFixed(0)} CFM</span>
        </div>
        <div className="flex justify-between">
          <span>Intake / Exhaust:</span>
          <span className="text-zinc-200">{metrics.intakeCfm.toFixed(0)} / {metrics.exhaustCfm.toFixed(0)} CFM</span>
        </div>
        <div className="flex justify-between">
          <span>Est. Temp:</span>
          <span className="text-zinc-200">{metrics.roomTempF.toFixed(1)}°F</span>
        </div>
        <div className="flex justify-between">
          <span>Est. RH:</span>
          <span className="text-zinc-200">{metrics.roomRH}%</span>
        </div>
        <div className="flex justify-between">
          <span>Room PPFD:</span>
          <span className="text-zinc-200">{metrics.roomPPFD.toFixed(0)} μmol/m²/s</span>
        </div>
        <div className="flex justify-between">
          <span>AC Sizing:</span>
          <span className="text-zinc-300 font-semibold">{Math.round(metrics.acSizingBtuHr)} BTU/hr</span>
        </div>
        <div className="flex justify-between">
          <span>Dehumidifier:</span>
          <span className="text-zinc-300 font-semibold">{metrics.dehumidifierPintsDay.toFixed(1)} Pints/day</span>
        </div>
        <div className="flex justify-between">
          <span>Sensible Heat:</span>
          <span className="text-zinc-500">{Math.round(metrics.sensibleHeatGainsBtuHr)} BTU/hr</span>
        </div>
        <div className="flex justify-between">
          <span>Latent Cooling:</span>
          <span className="text-emerald-500/80">{Math.round(metrics.latentCoolingBtuHr)} BTU/hr</span>
        </div>
      </div>
    </div>
  );
}

function ReservoirCard({ part }: { part: PlacedPart }) {
  const setParam = useBuilder((s) => s.setParam);
  const parts = useBuilder((s) => s.parts);
  const growthStage = useBuilder((s) => s.growthStage);
  const sim = useSimulation();
  const stats = useMemo(() => computeReservoirWaterStats(part, parts), [part, parts]);
  const runtime = sim.reservoirRuntime[part.id];

  const { ecLow, ecHigh } = useMemo(() => {
    if (growthStage === 'seedling') return { ecLow: 0.4, ecHigh: 0.8 };
    if (growthStage === 'flowering') return { ecLow: 1.5, ecHigh: 2.2 };
    return { ecLow: 1.0, ecHigh: 1.6 }; // vegetative
  }, [growthStage]);

  const isEcAlert = stats.ec < ecLow || stats.ec > ecHigh;

  return (
    <div className="rounded-lg border border-sky-900/50 bg-sky-950/20 p-3 space-y-3">
      <div className="flex items-center justify-between">
        <span className="text-[10px] font-bold tracking-wider text-sky-400 uppercase">Reservoir Health</span>
        <span className={`font-mono text-xs ${runtime?.empty ? 'text-rose-300' : 'text-sky-300'}`}>
          {(runtime?.gallonsCurrent ?? stats.volGal).toFixed(1)} Gal
        </span>
      </div>

      <div className="grid grid-cols-3 gap-2">
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">Runtime</div>
          <div className={`mt-0.5 text-xs font-bold ${runtime?.empty ? 'text-rose-400' : runtime?.minutesRemaining !== null ? 'text-amber-400' : 'text-emerald-400'}`}>
            {runtime
              ? runtime.minutesRemaining === null
                ? 'Stable'
                : runtime.empty
                  ? 'Dry'
                  : `${runtime.minutesRemaining.toFixed(1)} min`
              : 'Stable'}
          </div>
          <div className="text-[8px] text-zinc-600">
            {runtime?.minutesRemaining === null
              ? 'No net draw'
              : runtime?.empty
                ? 'Source exhausted'
                : `${runtime.netOutflowGph.toFixed(1)} GPH net draw`}
          </div>
        </div>

        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">Volume</div>
          <div className="mt-0.5 text-xs font-bold text-sky-300">
            {runtime
              ? `${runtime.gallonsCurrent.toFixed(1)} / ${runtime.gallonsCapacity.toFixed(1)}`
              : `${stats.volGal.toFixed(1)} / ${stats.volGal.toFixed(1)}`}
          </div>
          <div className="text-[8px] text-zinc-600">Gallons</div>
        </div>

        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">Line Hold</div>
          <div className="mt-0.5 text-xs font-bold text-zinc-200">
            {runtime ? runtime.gallonsHeldInSystem.toFixed(2) : '0.00'}
          </div>
          <div className="text-[8px] text-zinc-600">Gal in plumbing</div>
        </div>
      </div>

      {/* Water Stats Grid */}
      <div className="grid grid-cols-3 gap-2">
        {/* Temp */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">Temp</div>
          <div
            className={`mt-0.5 text-xs font-bold ${
              stats.tempF > 73.0 ? 'text-amber-400' : 'text-emerald-400'
            }`}
          >
            {stats.tempF.toFixed(1)}°F
          </div>
          <div className="text-[8px] text-zinc-600">
            {stats.tempF > 73.0 ? 'Too Warm' : 'Optimal'}
          </div>
        </div>

        {/* pH */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">pH</div>
          <div
            className={`mt-0.5 text-xs font-bold ${
              stats.ph < 5.5 || stats.ph > 6.5 ? 'text-amber-400' : 'text-emerald-400'
            }`}
          >
            {stats.ph.toFixed(1)}
          </div>
          <div className="text-[8px] text-zinc-600">
            {stats.ph < 5.5 || stats.ph > 6.5 ? 'Lockout' : 'Optimal'}
          </div>
        </div>

        {/* EC */}
        <div className="rounded-md bg-zinc-900/80 p-2 text-center">
          <div className="text-[9px] text-zinc-500 font-bold uppercase">EC</div>
          <div
            className={`mt-0.5 text-xs font-bold ${
              isEcAlert ? 'text-amber-400' : 'text-emerald-400'
            }`}
          >
            {stats.ec.toFixed(1)}
          </div>
          <div className="text-[8px] text-zinc-600">
            {isEcAlert ? 'Alert' : 'Optimal'}
          </div>
        </div>
      </div>

      <QuickSlider
        label="Adjust pH"
        unit=""
        value={part.params.ph ?? 6.0}
        meta={PART_DEFS.reservoir.params.ph}
        onChange={(v) => setParam(part.id, 'ph', v)}
      />
      <QuickSlider
        label="Adjust EC"
        unit="mS/cm"
        value={part.params.ec ?? 1.2}
        meta={PART_DEFS.reservoir.params.ec}
        onChange={(v) => setParam(part.id, 'ec', v)}
      />
    </div>
  );
}

function QuickSlider({
  label,
  unit,
  value,
  meta,
  onChange,
}: {
  label: string;
  unit: string;
  value: number;
  meta: { min: number; max: number; step: number };
  onChange: (v: number) => void;
}) {
  return (
    <div className="mt-1.5">
      <div className="mb-0.5 flex justify-between text-[11px]">
        <span className="text-zinc-400">{label}</span>
        <span className="font-mono text-sky-400">
          {value} {unit}
        </span>
      </div>
      <input
        type="range"
        min={meta.min}
        max={meta.max}
        step={meta.step}
        value={value}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        className="w-full accent-sky-500"
      />
    </div>
  );
}

// ---------------------------------------------------------------------------
// Single-part inspector
// ---------------------------------------------------------------------------

function PartInspector({ partId }: { partId: string }) {
  const part = useBuilder((s) => s.parts[partId]);
  const parts = useBuilder((s) => s.parts);
  const updatePart = useBuilder((s) => s.updatePart);
  const setParam = useBuilder((s) => s.setParam);
  const removeParts = useBuilder((s) => s.removeParts);
  const duplicateParts = useBuilder((s) => s.duplicateParts);
  const rotateParts = useBuilder((s) => s.rotateParts);
  const pushHistory = useBuilder((s) => s.pushHistory);
  const sim = useSimulation();

  if (!part) return null;
  const def = PART_DEFS[part.type];
  const netPotStatus = part.type === 'netPot' ? sim.netPots.find((p) => p.partId === partId) : undefined;
  const sourcePart = netPotStatus ? parts[netPotStatus.sourcePartId] : undefined;

  const setPos = (axis: number, v: number) => {
    pushHistory(`pos:${partId}:${axis}`);
    const position = [...part.position] as Vec3;
    position[axis] = axis === 1 ? Math.max(0, v) : v;
    updatePart(partId, { position });
  };

  const setRot = (axis: number, degVal: number) => {
    pushHistory(`rot:${partId}:${axis}`);
    const rotation = [...part.rotation] as Vec3;
    rotation[axis] = deg2rad(degVal);
    updatePart(partId, { rotation });
  };

  const paramEntries = Object.entries(def.params).filter(
    ([key]) =>
      (part.type !== 'pump' || !PUMP_CARD_KEYS.has(key)) &&
      (part.type !== 'airCompressor' || !AIR_COMPRESSOR_CARD_KEYS.has(key)) &&
      (part.type !== 'reservoir' || !RESERVOIR_CARD_KEYS.has(key)),
  );

  return (
    <div className="flex-1 space-y-3 overflow-y-auto px-3.5 py-2.5">
      <div>
        <div className="mb-1 flex items-center gap-2">
          <span
            className="h-3 w-3 rounded"
            style={{ backgroundColor: part.color ?? def.color }}
          />
          <span className="text-sm font-semibold text-zinc-200">{def.label}</span>
          <span className="ml-auto font-mono text-[10px] text-zinc-600">#{part.id}</span>
        </div>
        <input
          value={part.label ?? ''}
          placeholder="Name this part…"
          onChange={(e) => updatePart(partId, { label: e.target.value })}
          className="w-full rounded-md border border-zinc-800 bg-zinc-900 px-2 py-1.5 text-xs text-zinc-200 placeholder-zinc-600 outline-none focus:border-sky-600"
        />
      </div>

      {part.type === 'pipe' && (
        <div className="rounded-lg border border-zinc-800 bg-zinc-900/30 p-2.5 text-[11px] leading-snug text-zinc-400 mt-2">
          <span className="font-semibold text-zinc-300">Tip:</span> Drag the glowing blue ends of the pipe in the 3D view to stretch its length.
        </div>
      )}

      {part.type === 'pump' && <PumpCard part={part} />}
      {part.type === 'airCompressor' && <AirCompressorCard part={part} />}
      {part.type === 'reservoir' && <ReservoirCard part={part} />}
      {part.type === 'growTent' && <GrowTentCard part={part} />}
      {part.type === 'netPot' && netPotStatus && (
        <Section title="Hydraulics">
          <div
            className={`rounded-lg border px-3 py-2.5 ${
              netPotStatus.overflowing
                ? 'border-rose-900/70 bg-rose-950/40'
                : 'border-amber-900/60 bg-amber-950/30'
            }`}
          >
            <div
              className={`text-xs font-semibold ${
                netPotStatus.overflowing ? 'text-rose-300' : 'text-amber-300'
              }`}
            >
              {netPotStatus.overflowing ? 'Pot overflow active' : 'Pot near overflow'}
            </div>
            <p className="mt-1 text-[11px] leading-snug text-zinc-400">
              Water under this pot is coming from {sourcePart ? PART_DEFS[sourcePart.type].label : 'the flooded line'} at{' '}
              {netPotStatus.inflowGph.toFixed(0)} GPH. Water height is {netPotStatus.waterHeightFt.toFixed(2)} ft and the rim is {netPotStatus.rimHeightFt.toFixed(2)} ft.
            </p>
            <p className="mt-1.5 rounded-md bg-zinc-900/80 px-2 py-1 text-[11px] leading-snug text-zinc-300">
              <span className="font-semibold text-sky-400">Fix: </span>
              Add drainage or move the pot off the backed-up run.
            </p>
          </div>
        </Section>
      )}

      <Section title="Position (ft)">
        <div className="grid grid-cols-3 gap-1.5">
          {(['X', 'Y', 'Z'] as const).map((axis, i) => (
            <NumberField
              key={axis}
              label={axis}
              value={part.position[i]}
              step={0.25}
              onChange={(v) => setPos(i, v)}
            />
          ))}
        </div>
      </Section>

      <Section title="Rotation (°)">
        <div className="grid grid-cols-3 gap-1.5">
          {(['X', 'Y', 'Z'] as const).map((axis, i) => (
            <NumberField
              key={axis}
              label={axis}
              value={rad2deg(part.rotation[i])}
              step={15}
              onChange={(v) => setRot(i, v)}
            />
          ))}
        </div>
        <div className="mt-1.5 flex gap-1.5">
          <MiniBtn onClick={() => rotateParts([partId], 1, Math.PI / 2)}>⟳ 90° Y</MiniBtn>
          <MiniBtn onClick={() => rotateParts([partId], 2, Math.PI / 2)}>⟳ 90° Z</MiniBtn>
          <MiniBtn onClick={() => updatePart(partId, { rotation: [0, 0, 0] })}>Reset</MiniBtn>
        </div>
      </Section>

      {paramEntries.length > 0 && (
        <Section title="Parameters">
          <div className="space-y-2.5">
            {paramEntries.map(([key, meta]) => (
              <div key={key}>
                <div className="mb-0.5 flex justify-between text-[11px]">
                  <span className="text-zinc-400">{meta.label}</span>
                  <span className="font-mono text-sky-400">
                    {part.params[key] ?? def.defaults[key]}
                    {meta.unit ? ` ${meta.unit}` : ''}
                  </span>
                </div>
                {key === 'diameter' || key === 'diameterA' || key === 'diameterB' ? (
                  <select
                    value={String(snapPipeSize(part.params[key] ?? def.defaults[key]))}
                    onChange={(e) => setParam(partId, key, parseFloat(e.target.value))}
                    className="w-full rounded-md border border-zinc-800 bg-zinc-900 px-2 py-1 text-[11px] text-zinc-200 outline-none focus:border-sky-600"
                  >
                    {STANDARD_PIPE_SIZES.map((size) => (
                      <option key={size} value={size}>
                        {size} in
                      </option>
                    ))}
                  </select>
                ) : (
                  <input
                    type="range"
                    min={meta.min}
                    max={meta.max}
                    step={meta.step}
                    value={part.params[key] ?? def.defaults[key]}
                    onChange={(e) => setParam(partId, key, parseFloat(e.target.value))}
                    className="w-full accent-sky-500"
                  />
                )}
              </div>
            ))}
          </div>
        </Section>
      )}

      {ELECTRICAL_PARTS.has(part.type) && (
        <Section title="Smart Timer Schedule">
          <div className="space-y-2 rounded-lg border border-zinc-800 bg-zinc-900/20 p-2.5">
            <label className="flex items-center justify-between text-[11px] cursor-pointer">
              <span className="text-zinc-400 font-semibold">Enable Timer</span>
              <input
                type="checkbox"
                checked={(part.params.timerEnabled ?? 0) === 1}
                onChange={(e) => setParam(partId, 'timerEnabled', e.target.checked ? 1 : 0)}
                className="h-3.5 w-3.5 accent-sky-500 rounded border-zinc-800 bg-zinc-900 cursor-pointer"
              />
            </label>
            
            {(part.params.timerEnabled ?? 0) === 1 && (
              <div className="grid grid-cols-2 gap-2 pt-1 border-t border-zinc-900/50">
                <div>
                  <div className="mb-1 text-[9px] font-bold text-zinc-500 uppercase">ON Duration</div>
                  <label className="flex items-center gap-1 rounded-md border border-zinc-800 bg-zinc-900 px-1.5 py-0.5">
                    <input
                      type="number"
                      min={1}
                      value={part.params.timerOnMin ?? 15}
                      onChange={(e) => {
                        const v = parseInt(e.target.value, 10);
                        if (!Number.isNaN(v) && v > 0) setParam(partId, 'timerOnMin', v);
                      }}
                      className="w-full bg-transparent text-left font-mono text-[11px] text-zinc-200 outline-none"
                    />
                    <span className="text-[10px] text-zinc-500">min</span>
                  </label>
                  <span className="text-[9px] text-zinc-500 block mt-0.5">
                    {formatDurationHelper(part.params.timerOnMin ?? 15)}
                  </span>
                </div>
                <div>
                  <div className="mb-1 text-[9px] font-bold text-zinc-500 uppercase">OFF Duration</div>
                  <label className="flex items-center gap-1 rounded-md border border-zinc-800 bg-zinc-900 px-1.5 py-0.5">
                    <input
                      type="number"
                      min={1}
                      value={part.params.timerOffMin ?? 45}
                      onChange={(e) => {
                        const v = parseInt(e.target.value, 10);
                        if (!Number.isNaN(v) && v > 0) setParam(partId, 'timerOffMin', v);
                      }}
                      className="w-full bg-transparent text-left font-mono text-[11px] text-zinc-200 outline-none"
                    />
                    <span className="text-[10px] text-zinc-500">min</span>
                  </label>
                  <span className="text-[9px] text-zinc-500 block mt-0.5">
                    {formatDurationHelper(part.params.timerOffMin ?? 45)}
                  </span>
                </div>
              </div>
            )}
          </div>
        </Section>
      )}

      <Section title="Color">
        <ColorSwatchRow ids={[partId]} />
      </Section>

      <div className="flex gap-2 pt-1">
        <button
          onClick={() => duplicateParts([partId])}
          title="Duplicate this part (D)"
          className="flex-1 rounded-md border border-zinc-700 bg-zinc-800 px-2 py-1 text-[11px] font-medium text-zinc-200 hover:bg-zinc-700"
        >
          Duplicate
        </button>
        <button
          onClick={() => removeParts([partId])}
          title="Delete this part (⌫)"
          className="flex-1 rounded-md border border-rose-900 bg-rose-950 px-2 py-1 text-[11px] font-medium text-rose-300 hover:bg-rose-900"
        >
          Delete
        </button>
      </div>
    </div>
  );
}

function Section({ title, children }: { title: string; children: React.ReactNode }) {
  return (
    <div>
      <h3 className="mb-1.5 text-[10px] font-bold tracking-wider text-zinc-500 uppercase">
        {title}
      </h3>
      {children}
    </div>
  );
}

function NumberField({
  label,
  value,
  step,
  onChange,
}: {
  label: string;
  value: number;
  step: number;
  onChange: (v: number) => void;
}) {
  return (
    <label className="flex items-center gap-1 rounded-md border border-zinc-800 bg-zinc-900 px-1.5">
      <span className="text-[10px] font-bold text-zinc-600">{label}</span>
      <input
        type="number"
        value={Number(value.toFixed(2))}
        step={step}
        onChange={(e) => {
          const v = parseFloat(e.target.value);
          if (!Number.isNaN(v)) onChange(v);
        }}
        className="w-full bg-transparent py-1 text-right font-mono text-[11px] text-zinc-200 outline-none"
      />
    </label>
  );
}

function MiniBtn({ children, onClick }: { children: React.ReactNode; onClick: () => void }) {
  return (
    <button
      onClick={onClick}
      className="flex-1 rounded border border-zinc-800 bg-zinc-900 px-1 py-0.5 text-[10px] text-zinc-300 hover:bg-zinc-800"
    >
      {children}
    </button>
  );
}
