import { useEffect, useState, useMemo } from 'react';
import { SceneCanvas, clientToGround } from './components/SceneCanvas';
import { PartLibrary } from './components/PartLibrary';
import { PropertiesPanel } from './components/PropertiesPanel';
import { Toolbar } from './components/Toolbar';
import { StatusPanel } from './components/StatusPanel';
import { ColorPopover } from './components/ColorPopover';
import { BomPanel } from './bom';
import { DesignCheckPanel, validateSystem } from './validation';
import { getHotbarPartForKey } from './parts/hotbar';
import { useSimulation } from './simulation/flowSimulator';
import { snapValue, useBuilder } from './store/builderStore';
import { autosave, loadAutosave } from './utils/serializer';
import { demoParts } from './utils/demoProject';
import type { PartType } from './types';
import type { Finding } from './validation';
import { AuthProvider, useAuth } from './auth/AuthContext';
import { useCollab } from './hooks/useCollab';
import { LoginModal } from './auth/LoginModal';
import { ToastContainer, addToast } from './components/Toast';
import { useSaveStatus } from './store/saveStatusStore';
import { TestimonialScroller } from './components/TestimonialScroller';
import { ScheduleDashboard } from './components/ScheduleDashboard';

/** Restore the autosaved design, or seed the demo system on first visit. */
/** Restore the autosaved design, or seed the demo system on first visit. */
function useBootstrap(email?: string, loading?: boolean) {
  const { user } = useAuth();
  const hasUpgraded = user && user.purchased_slots > 0;

  useEffect(() => {
    if (loading) return;
    const saved = hasUpgraded ? loadAutosave(email) : null;
    if (saved && saved.parts.length > 0) {
      useBuilder.getState().loadParts(saved.parts, saved.name);
    } else {
      useBuilder.getState().loadParts(demoParts(), 'Demo: pump loop');
    }
  }, [email, loading, hasUpgraded]);
}

/** Debounced autosave whenever parts change. */
function useAutosave(email?: string, loading?: boolean) {
  const { user } = useAuth();
  const hasUpgraded = user && user.purchased_slots > 0;
  const setStatus = useSaveStatus((s) => s.setStatus);

  useEffect(() => {
    if (loading) return;
    if (!hasUpgraded) {
      setStatus('unsaved');
      return;
    }

    let timer: ReturnType<typeof setTimeout> | undefined;
    let hasUnsavedChanges = false;
    const unsub = useBuilder.subscribe((s, prev) => {
      if (s.parts !== prev.parts) {
        hasUnsavedChanges = true;
        setStatus('unsaved');
      }
      const isDragging = s.draggingId !== null || s.gizmoDragging;
      const wasDragging = prev.draggingId !== null || prev.gizmoDragging;
      const dragEnded = wasDragging && !isDragging;
      const partsChangedWhileNotDragging = (s.parts !== prev.parts) && !isDragging;

      if (hasUnsavedChanges && (dragEnded || partsChangedWhileNotDragging)) {
        clearTimeout(timer);
        setStatus('saving');
        timer = setTimeout(() => {
          autosave(useBuilder.getState().projectName, Object.values(useBuilder.getState().parts), email);
          hasUnsavedChanges = false;
          setStatus('saved');
        }, 600);
      } else if (isDragging) {
        clearTimeout(timer);
        timer = undefined;
      }
    });
    return () => {
      unsub();
      clearTimeout(timer);
    };
  }, [email, loading, hasUpgraded, setStatus]);
}

/** Global keyboard shortcuts. */
function useShortcuts() {
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      const target = e.target as HTMLElement;
      if (/^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName)) return;
      const s = useBuilder.getState();
      const mod = e.metaKey || e.ctrlKey;

      if (mod && e.key.toLowerCase() === 'z') {
        e.preventDefault();
        e.shiftKey ? s.redo() : s.undo();
        return;
      }
      if (mod && e.key.toLowerCase() === 'y') {
        e.preventDefault();
        s.redo();
        return;
      }
      if (mod && e.key.toLowerCase() === 'a') {
        e.preventDefault();
        s.selectAll();
        return;
      }
      if (mod && e.key.toLowerCase() === 'c') {
        const selectedParts = s.selectedIds.map(id => s.parts[id]).filter(Boolean);
        if (selectedParts.length > 0) {
          e.preventDefault();
          navigator.clipboard.writeText(JSON.stringify({ app: 'hydro-builder-clip', parts: selectedParts }));
          addToast(`Copied ${selectedParts.length} parts`, 'info');
        }
        return;
      }
      if (mod && e.key.toLowerCase() === 'v') {
        e.preventDefault();
        navigator.clipboard.readText().then((text) => {
          try {
            const data = JSON.parse(text);
            if (data && data.app === 'hydro-builder-clip' && Array.isArray(data.parts)) {
              const pastedIds = s.pasteParts(data.parts);
              addToast(`Pasted ${pastedIds.length} parts`, 'success');
            } else {
              addToast('Clipboard does not contain valid hydro parts', 'error');
            }
          } catch (err) {
            addToast(err instanceof Error ? `Paste failed: ${err.message}` : 'Clipboard does not contain valid hydro parts', 'error');
          }
        }).catch(() => {
          addToast('Could not read clipboard', 'error');
        });
        return;
      }
      if (mod) return; // don't hijack other browser shortcuts

      const hotbarType = getHotbarPartForKey(e.key);
      if (hotbarType) {
        e.preventDefault();
        if (s.tool !== 'select') s.setTool('select');
        s.setPlacingType(s.placingType === hotbarType ? null : hotbarType);
        return;
      }

      switch (e.key) {
        case ' ':
          e.preventDefault(); // keep Space from scrolling / re-clicking buttons
          s.toggleSimRunning();
          break;
        case 'Escape':
          if (s.colorPopover) {
            s.closeColorPopover();
          } else {
            s.setTool('select');
            s.setSelected(null);
          }
          break;
        case 'Delete':
        case 'Backspace':
          s.removeParts(s.selectedIds);
          break;
        case 'r':
        case 'R':
          s.rotateParts(s.selectedIds, 1, (e.shiftKey ? -1 : 1) * (Math.PI / 2));
          break;
        case 'd':
        case 'D':
          e.preventDefault();
          s.duplicateParts(s.selectedIds);
          break;
        case 'q':
        case 'Q':
          s.translateParts(s.selectedIds, [0, -s.gridSize, 0], 'nudge');
          break;
        case 'e':
        case 'E':
          s.translateParts(s.selectedIds, [0, s.gridSize, 0], 'nudge');
          break;
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);
}

import { PaywallModal } from './payments/PaywallModal';

function BuilderApp() {
  const { user, loading: authLoading } = useAuth();
  const [showPaywall, setShowPaywall] = useState(false);
  const email = user?.email;

  useBootstrap(email, authLoading);
  useAutosave(email, authLoading);
  useShortcuts();
  const { remoteCursors } = useCollab();
  const placingType = useBuilder((s) => s.placingType);
  const tool = useBuilder((s) => s.tool);
  const selectedCount = useBuilder((s) => s.selectedIds.length);
  const viewMode = useBuilder((s) => s.viewMode);
  const renderMode = useBuilder((s) => s.renderMode);
  const parts = useBuilder((s) => s.parts);
  const activeRoomId = useBuilder((s) => s.activeRoomId);
  const sim = useSimulation();
  const [bomOpen, setBomOpen] = useState(false);
  const [dashboardOpen, setDashboardOpen] = useState(false);
  const [checkFindings, setCheckFindings] = useState<Finding[] | null>(null);

  const roomParts = useMemo(() => {
    return Object.values(parts).filter((p) => p.roomId === activeRoomId);
  }, [parts, activeRoomId]);

  const runDesignCheck = () => {
    setCheckFindings(validateSystem(roomParts, sim));
  };

  const applyFindingFix = (finding: Finding) => {
    const s = useBuilder.getState();

    if (finding.id.startsWith('dead-leg:')) {
      s.fixDeadLeg(finding.partIds);
    } else if (finding.id.startsWith('diameter-mismatch:')) {
      s.fixDiameterMismatch(finding.partIds);
    } else if (finding.id.startsWith('pump-dead:')) {
      const pumpId = finding.partIds[0];
      if (pumpId) {
        const p = s.parts[pumpId];
        if (p && (p.params.on === 0 || p.params.enabled === 0)) {
          s.updatePart(pumpId, { params: { ...p.params, on: 1, enabled: 1 } });
        } else {
          s.fixPumpDeadEnd(pumpId);
        }
      }
    } else if (finding.id.startsWith('pump-head:')) {
      const pumpId = finding.partIds[0];
      if (pumpId) {
        const p = s.parts[pumpId];
        if (p) {
          const requiredHead = Math.ceil((p.params.maxHeadFt || 8) * 1.5);
          s.updatePart(pumpId, { params: { ...p.params, maxHeadFt: Math.min(30, requiredHead) } });
        }
      }
    } else if (finding.id.startsWith('unsupported-span:')) {
      const pipeId = finding.partIds[0];
      if (pipeId) {
        const p = s.parts[pipeId];
        if (p) {
          const y = p.position[1];
          const supportHeight = Math.max(0.5, y - 0.1);
          const latticeId = s.addPart('lattice', [p.position[0], supportHeight / 2, p.position[2]], { snap: false });
          const lattice = useBuilder.getState().parts[latticeId];
          if (lattice) {
            s.updatePart(latticeId, { params: { ...lattice.params, height: supportHeight } });
          }
        }
      }
    } else if (finding.id.startsWith('drain-capacity:')) {
      const drainId = finding.partIds[0];
      if (drainId) {
        const p = s.parts[drainId];
        if (p) {
          const reported = sim.drains?.find((d) => d?.partId === drainId)?.inflowGph;
          const inflow = reported ?? sim.flows?.[drainId] ?? 0;
          const needed = Math.ceil(inflow / 25) * 25;
          s.updatePart(drainId, { params: { ...p.params, capacityGph: Math.min(2000, needed) } });
        }
      }
    } else if (finding.id.startsWith('res-temp-high:')) {
      const chillers = Object.values(s.parts).filter((p) => p.type === 'waterChiller');
      for (const chiller of chillers) {
        s.updatePart(chiller.id, { params: { ...chiller.params, on: 1, targetTemp: 68 } });
      }
    } else if (finding.id.startsWith('res-ph-low:') || finding.id.startsWith('res-ph-high:')) {
      const resId = finding.partIds[0];
      if (resId) {
        const p = s.parts[resId];
        if (p) s.updatePart(resId, { params: { ...p.params, ph: 6.0 } });
      }
    } else if (finding.id.startsWith('res-ec-low:') || finding.id.startsWith('res-ec-high:')) {
      const resId = finding.partIds[0];
      if (resId) {
        const p = s.parts[resId];
        if (p) s.updatePart(resId, { params: { ...p.params, ec: 1.2 } });
      }
    } else if (finding.id.startsWith('room-co2-open:') || finding.id.startsWith('room-co2-missing:') || finding.id.startsWith('room-co2-starved:')) {
      const tentId = finding.partIds[0];
      if (tentId) {
        const p = s.parts[tentId];
        if (p) {
          // If the CO2 open warning is active, seal the room. If CO2 is starved/missing, unseal the room to swap with fresh air.
          const isCo2Open = finding.id.startsWith('room-co2-open:');
          s.updatePart(tentId, { params: { ...p.params, sealed: isCo2Open ? 1 : 0 } });
        }
      }
    } else if (finding.id.startsWith('room-sealed-exhaust:')) {
      const tentId = finding.partIds[0];
      if (tentId) {
        const p = s.parts[tentId];
        if (p) s.updatePart(tentId, { params: { ...p.params, sealed: 0 } });
      }
    } else if (finding.id.startsWith('room-vent-low:')) {
      const fans = Object.values(s.parts).filter((p) => p.type === 'inlineFan');
      for (const fan of fans) {
        s.updatePart(fan.id, { params: { ...fan.params, cfm: 600 } });
      }
    } else if (finding.id.startsWith('room-light-low:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, watts: Math.min(1000, (light.params.watts ?? 650) + 150) } });
      }
    } else if (finding.id.startsWith('room-light-high:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, watts: Math.max(100, (light.params.watts ?? 650) - 200) } });
      }
    } else if (finding.id.startsWith('room-dli-low:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, photoperiod: Math.min(24, (light.params.photoperiod ?? 18) + 4) } });
      }
    } else if (finding.id.startsWith('room-dli-high:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, photoperiod: Math.max(0, (light.params.photoperiod ?? 18) - 6) } });
      }
    } else if (finding.id.startsWith('room-vpd-low:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, watts: Math.min(1000, (light.params.watts ?? 650) + 150) } });
      }
    } else if (finding.id.startsWith('room-vpd-high:')) {
      const lights = Object.values(s.parts).filter((p) => p.type === 'growLight');
      for (const light of lights) {
        s.updatePart(light.id, { params: { ...light.params, watts: Math.max(100, (light.params.watts ?? 650) - 150) } });
      }
    } else if (finding.id.startsWith('closed-valve:')) {
      const valveId = finding.partIds[0];
      if (valveId) {
        const p = s.parts[valveId];
        if (p) s.updatePart(valveId, { params: { ...p.params, open: 100 } });
      }
    } else if (finding.id.startsWith('part-overlap:')) {
      const ids = finding.partIds;
      if (ids.length >= 2) {
        s.removeParts([ids[1]]);
      }
    } else if (finding.id.startsWith('nutrient-depletion:')) {
      const resId = finding.partIds[0];
      if (resId) {
        const p = s.parts[resId];
        if (p) {
          s.updatePart(resId, {
            params: {
              ...p.params,
              width: Math.min(5, (p.params.width ?? 2) * 1.5),
              depth: Math.min(4, (p.params.depth ?? 1.4) * 1.5),
              height: Math.min(3, (p.params.height ?? 1) * 1.2),
            }
          });
        }
      }
    } else if (finding.id.startsWith('orphan:')) {
      s.removeParts(finding.partIds);
    }

    setTimeout(() => {
      const nextParts = Object.values(useBuilder.getState().parts);
      setCheckFindings(validateSystem(nextParts, sim));
    }, 50);
  };

  /** Handle HTML5 drag-and-drop from the parts library onto the canvas. */
  const onDrop = (e: React.DragEvent) => {
    const type = e.dataTransfer.getData('application/x-hydro-part') as PartType;
    if (!type) return;
    e.preventDefault();
    const ground = clientToGround(e.clientX, e.clientY);
    if (!ground) return;
    const s = useBuilder.getState();
    const snap = (v: number) => (s.snapToGrid ? snapValue(v, s.gridSize) : v);
    const y = ['pipe', 'elbow', 'tee', 'valve', 'emitter'].includes(type) ? 0.25 : 0;
    s.addPart(type, [snap(ground[0]), y, snap(ground[2])]);
  };

  if (authLoading) {
    return (
      <div className="flex h-screen flex-col items-center justify-center bg-zinc-950 text-zinc-100">
        <div className="h-8 w-8 animate-spin rounded-full border-4 border-sky-500 border-t-transparent mb-4" />
        <span className="text-sm font-semibold tracking-wider text-zinc-400">Hydro Builder initializing...</span>
      </div>
    );
  }

  if (!user) {
    return <LoginModal />;
  }

  return (
    <div className="flex h-screen flex-col bg-zinc-950 text-zinc-100 select-none">
      <Toolbar 
        onOpenBom={() => setBomOpen(true)} 
        onCheckBuild={runDesignCheck} 
        onOpenDashboard={() => setDashboardOpen(true)} 
      />
      <div className="flex min-h-0 flex-1">
        <PartLibrary />
        <main
          className="relative min-w-0 flex-1"
          onDragOver={(e) => e.preventDefault()}
          onDrop={onDrop}
        >
          <SceneCanvas remoteCursors={remoteCursors} />
          {tool === 'measure' && (
            <div className="absolute top-4 left-1/2 -translate-x-1/2 rounded-full border border-sky-500/30 bg-zinc-950/80 px-4 py-1.5 text-xs font-semibold text-sky-400 shadow-lg backdrop-blur">
              📏 Measurement mode: Click two points in the 3D scene
              <button
                onClick={() => useBuilder.getState().setTool('select')}
                className="ml-3 rounded bg-sky-900/50 px-2 py-0.5 text-sky-200 hover:bg-sky-800/50"
              >
                Done
              </button>
            </div>
          )}
          {/* Premium Upsell Bar */}
          {!user || (user as any).purchased_slots === 0 ? (
            <div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex items-center justify-between gap-4 rounded-xl border border-amber-500/30 bg-gradient-to-r from-zinc-950/95 via-amber-950/40 to-zinc-950/95 px-6 py-3 shadow-2xl backdrop-blur">
              <div>
                <h4 className="text-sm font-bold text-amber-400 tracking-wide uppercase">Unlock Pro Premium</h4>
                <p className="text-[11px] text-zinc-300 mt-0.5">One-time $5 special offer. Unlimited projects, PDF shopping lists, CAD exports, and drjonesbot.</p>
              </div>
              <button 
                onClick={() => setShowPaywall(true)}
                className="whitespace-nowrap rounded-lg bg-amber-500 px-4 py-2 text-xs font-bold text-amber-950 transition hover:bg-amber-400 hover:scale-105"
              >
                Join Now
              </button>
            </div>
          ) : null}
          {/* Contextual hint overlay */}
          {(placingType || tool === 'measure' || tool === 'pipeRun') && (
            <div className="pointer-events-none absolute top-3 left-1/2 -translate-x-1/2 rounded-full border border-sky-800 bg-sky-950/90 px-4 py-1.5 text-xs font-medium text-sky-200 shadow-lg">
              {placingType
                ? 'Click to place · Shift-click to stamp copies · 1-9 switches parts · Esc to cancel'
                : tool === 'pipeRun'
                  ? 'Pipe Run: click points · Shift-click to keep routing · Esc to exit'
                  : 'Measure: click two points · Esc to exit'}
            </div>
          )}
          {/* Selection hint */}
          {!placingType && tool !== 'measure' && selectedCount > 0 && (
            <div className="pointer-events-none absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-zinc-800 bg-zinc-950/85 px-4 py-1.5 text-[11px] font-medium text-zinc-400 shadow-lg backdrop-blur">
              {selectedCount > 1
                ? `${selectedCount} parts — drag moves all · ⌥drag clones · ⇧click adds/removes · ⌘click color · D duplicate · ⌫ delete`
                : `Drag body or gizmo to move · double-click plumbing to grab the whole run · selected pipes show stretch handles · drag a connector dot to snap${viewMode === 'orbit' ? ' · ⇧drag for vertical' : ''} · ${renderMode === 'xray' ? 'X-Ray shows internals' : renderMode === 'water' ? 'Water mode isolates flow' : 'Builder mode keeps solids primary'} · ⌥drag clones · ⌘click color · R rotate`}
            </div>
          )}
          <ColorPopover />
        </main>
        <PropertiesPanel />
      </div>
      <StatusPanel />
      {bomOpen && <BomPanel onClose={() => setBomOpen(false)} />}
      {dashboardOpen && <ScheduleDashboard onClose={() => setDashboardOpen(false)} />}
      {checkFindings && (
        <DesignCheckPanel
          findings={checkFindings}
          onSelectPart={(id) => {
            const s = useBuilder.getState();
            s.setSelected(id);
            if (id) {
              const part = s.parts[id];
              if (part) s.setFocusTarget(part.position);
            }
          }}
          onApplyFix={applyFindingFix}
          onClose={() => setCheckFindings(null)}
        />
      )}
      <TestimonialScroller />
      <ToastContainer />
    </div>
  );
}

export default function App() {
  return (
    <AuthProvider>
      <BuilderApp />
    </AuthProvider>
  );
}
