// FloorCanvas — root app with history, persistence, multi-floor support

const { useState, useEffect, useRef, useMemo, useCallback } = React;

const DEFAULT_PLAN = JSON.parse('{"name":"Untitled Plan","floors":[{"id":"fl-basement","name":"Basement","level":-1,"shapes":[]},{"id":"fl-ground","name":"Ground","level":0,"shapes":[{"id":"2f9n6ak","type":"wall","a":{"x":0,"y":0},"b":{"x":40,"y":0},"thickness":0.8},{"id":"lugyes0","type":"wall","a":{"x":40,"y":0},"b":{"x":40,"y":30},"thickness":0.8},{"id":"h1i6poq","type":"wall","a":{"x":40,"y":30},"b":{"x":0,"y":30},"thickness":0.8},{"id":"seknj2f","type":"wall","a":{"x":0,"y":30},"b":{"x":0,"y":0},"thickness":0.8},{"id":"6mq0lmi","type":"wall","a":{"x":16,"y":15},"b":{"x":40,"y":15},"thickness":0.8},{"id":"75ks9bq","type":"window","x":40,"y":5,"width":4,"rotation":90},{"id":"cu3rcx4","type":"furniture","kind":"sofa-3","x":-1.5,"y":5,"w":7,"h":3,"rotation":-90},{"id":"5jffadp","type":"furniture","kind":"rug","x":5,"y":5.5,"w":3.5,"h":3.5,"rotation":0},{"id":"s51zefe","type":"furniture","kind":"kitchen-counter","x":34.5,"y":18.5,"w":8,"h":2,"rotation":90},{"id":"7x6lhz4","type":"furniture","kind":"kitchen-island","x":29,"y":21.5,"w":9,"h":3,"rotation":90},{"id":"2g1mimx","type":"furniture","kind":"bed-queen","x":31,"y":7,"w":5.5,"h":7.7,"rotation":180},{"id":"abzds02","type":"furniture","kind":"bath-sink","x":20.5,"y":8,"w":2,"h":1.6,"rotation":0},{"id":"9m55aw8","type":"label","x":9,"y":3,"text":"LIVING","dim":"","size":1,"rotation":0},{"id":"zeqmz26","type":"label","x":33.5,"y":17,"text":"KITCHEN","dim":"","size":1,"rotation":0},{"id":"zx82bff","type":"label","x":29,"y":3.5,"text":"BEDROOM","dim":"","size":1,"rotation":0},{"id":"vg507j6","type":"furniture","kind":"elev-modern","x":0,"y":38,"w":40,"h":12,"rotation":0},{"id":"s9d7wr0","type":"furniture","kind":"garage-elev","x":26,"y":40,"w":13,"h":9,"rotation":0},{"id":"tz1fso4","type":"furniture","kind":"garage-elev","x":1,"y":40,"w":13,"h":9,"rotation":0},{"id":"fi2c1i0","type":"furniture","kind":"desk","x":14,"y":40,"w":12,"h":10,"rotation":0},{"id":"zt7wxw0","type":"furniture","kind":"workbench","x":16,"y":44,"w":8.5,"h":3.5,"rotation":270},{"id":"snbjwta","type":"furniture","kind":"garage-door","x":26,"y":29.5,"w":13,"h":1,"rotation":0},{"id":"8aydj65","type":"furniture","kind":"garage-door","x":1,"y":29.5,"w":13,"h":1,"rotation":0},{"id":"e2vjsk6","type":"furniture","kind":"workbench","x":18,"y":29.5,"w":3.5,"h":1,"rotation":180},{"id":"gwet9ep","type":"window","x":40,"y":21,"width":3,"rotation":90},{"id":"5u159tc","type":"furniture","kind":"desk","x":26,"y":-0.5,"w":6,"h":1,"rotation":0},{"id":"pwdt1h1","type":"furniture","kind":"desk","x":32,"y":-0.5,"w":6,"h":1,"rotation":0},{"id":"qg60pmg","type":"furniture","kind":"kitchen-counter","x":34.5,"y":24.5,"w":8,"h":2,"rotation":90},{"id":"pefcbfb","type":"furniture","kind":"stove","x":37.5,"y":25.5,"w":2,"h":2,"rotation":0},{"id":"lcxm83y","type":"furniture","kind":"sink","x":36.5,"y":21.5,"w":3.8,"h":2,"rotation":90},{"id":"zbszqlz","type":"furniture","kind":"fridge","x":37,"y":15.5,"w":2.5,"h":2.5,"rotation":180},{"id":"qs6ksah","type":"wall","a":{"x":16,"y":14.5},"b":{"x":16,"y":0},"thickness":0.8},{"id":"gf39gv5","type":"wall","a":{"x":23,"y":15},"b":{"x":23,"y":7.5},"thickness":0.8},{"id":"ap1slj5","type":"wall","a":{"x":27.5,"y":7.5},"b":{"x":16,"y":7.5},"thickness":0.8},{"id":"91yecf5","type":"door","x":16,"y":11.5,"width":3,"rotation":90},{"id":"mepew2p","type":"furniture","kind":"toilet","x":18.5,"y":8,"w":1.6,"h":2.4,"rotation":0},{"id":"94l1n7o","type":"wall","a":{"x":27.5,"y":7.5},"b":{"x":27.5,"y":15},"thickness":0.8},{"id":"l9k5f8j","type":"furniture","kind":"whiteboard","x":25,"y":11,"w":5,"h":0.4,"rotation":90},{"id":"8hasbkv","type":"furniture","kind":"desk","x":1.5,"y":-0.5,"w":6,"h":1,"rotation":0},{"id":"09fbsin","type":"furniture","kind":"desk","x":7.5,"y":-0.5,"w":6,"h":1,"rotation":0},{"id":"trhm5s0","type":"furniture","kind":"dresser","x":21,"y":5.5,"w":6,"h":1.7,"rotation":0},{"id":"1b35r7j","type":"door","x":16,"y":0.5,"width":3,"rotation":90},{"id":"jwkjaa8","type":"furniture","kind":"nightstand","x":37.5,"y":13.5,"w":1.1,"h":1.1,"rotation":0},{"id":"ionxuji","type":"furniture","kind":"bar-stool","x":30,"y":19,"w":1.4,"h":1.4,"rotation":0},{"id":"ve4v6l2","type":"furniture","kind":"bar-stool","x":30,"y":21,"w":1.4,"h":1.4,"rotation":0},{"id":"4vyd2i7","type":"furniture","kind":"bar-stool","x":30,"y":23,"w":1.4,"h":1.4,"rotation":0},{"id":"j3dawqo","type":"furniture","kind":"bar-stool","x":30,"y":25,"w":1.4,"h":1.4,"rotation":0},{"id":"w48jxoo","type":"furniture","kind":"car","x":44,"y":14,"w":6,"h":14,"rotation":0},{"id":"5v2t81w","type":"wall","a":{"x":16,"y":15.5},"b":{"x":16,"y":20.5},"thickness":0.8},{"id":"fowr55f","type":"wall","a":{"x":16,"y":21},"b":{"x":27,"y":21},"thickness":0.8},{"id":"2o0vwbt","type":"wall","a":{"x":27.5,"y":21},"b":{"x":27.5,"y":15},"thickness":0.8},{"id":"aeqxrog","type":"furniture","kind":"washer","x":24.5,"y":18,"w":2.5,"h":2.5,"rotation":90},{"id":"972x2xc","type":"furniture","kind":"dryer","x":24.5,"y":15.5,"w":2.5,"h":2.5,"rotation":90},{"id":"0ee7zyl","type":"furniture","kind":"panel","x":20,"y":19.5,"w":2,"h":1.2,"rotation":0},{"id":"tqpzl9h","type":"door","x":16.5,"y":20.5,"width":3,"rotation":0},{"id":"kr85x08","type":"furniture","kind":"office-chair","x":16.5,"y":15.5,"w":2,"h":2,"rotation":0},{"id":"ygg8ogl","type":"furniture","kind":"shelving","x":18.5,"y":15.5,"w":4.5,"h":0.5,"rotation":0},{"id":"z6h8l57","type":"furniture","kind":"coffee-table","x":12.5,"y":6.5,"w":3.5,"h":1.5,"rotation":90},{"id":"imbfp9j","type":"furniture","kind":"tv","x":12,"y":7,"w":5,"h":0.5,"rotation":90},{"id":"sithbew","type":"furniture","kind":"loveseat","x":4.5,"y":10.5,"w":5,"h":3,"rotation":-180},{"id":"kajea9z","type":"furniture","kind":"bookshelf","x":20,"y":21.5,"w":7.5,"h":1.7,"rotation":0},{"id":"jxv3a0j","type":"furniture","kind":"piano","x":-1,"y":16.5,"w":5,"h":2,"rotation":-90},{"id":"sf1puxi","type":"furniture","kind":"shelving","x":15.5,"y":9,"w":2.5,"h":0.5,"rotation":-90},{"id":"5jybh7c","type":"furniture","kind":"nightstand","x":1.5,"y":11,"w":2.1,"h":2.1,"rotation":0},{"id":"9xbd3sk","type":"furniture","kind":"dining-table-6","x":3.5,"y":22.5,"w":6,"h":3.5,"rotation":0},{"id":"rj77pb6","type":"furniture","kind":"shower","x":19.5,"y":11.5,"w":3,"h":3,"rotation":0},{"id":"9k0ppew","type":"furniture","kind":"chair","x":13.5,"y":17.5,"w":1.6,"h":1.6,"rotation":270},{"id":"uz4ywa6","type":"furniture","kind":"desk","x":12.5,"y":17.5,"w":5,"h":1.5,"rotation":-90},{"id":"3fnhu8f","type":"label","x":6.5,"y":24,"text":"DINING","dim":"","size":1,"rotation":0},{"id":"cffvna1","type":"label","x":20,"y":26.5,"text":"FOYER","dim":"","size":1,"rotation":0},{"id":"ozwt48a","type":"label","x":18.5,"y":11.5,"text":"BATH","dim":"","size":1,"rotation":0},{"id":"gl5wflz","type":"label","x":21,"y":18,"text":"MECH","dim":"","size":0.85,"rotation":0},{"id":"n46wm60","type":"dimension","a":{"x":-0.5,"y":-1},"b":{"x":39.5,"y":-1}},{"id":"x2trnob","type":"dimension","a":{"x":-1,"y":-0.5},"b":{"x":-1,"y":29.5}},{"id":"0clmqv9","type":"dimension","a":{"x":0,"y":1},"b":{"x":16,"y":1}},{"id":"q6dtzje","type":"dimension","a":{"x":16,"y":1},"b":{"x":40,"y":1}},{"id":"wovhwe9","type":"dimension","a":{"x":27.5,"y":11},"b":{"x":40,"y":11}},{"id":"qxrledj","type":"dimension","a":{"x":39,"y":14.5},"b":{"x":39,"y":-0.5}}]},{"id":"fl-second","name":"Second","level":1,"shapes":[]}],"activeId":"fl-ground"}');

function makeInitialFloors() {
  return DEFAULT_PLAN.floors.map((f) => ({ ...f, shapes: f.shapes.map((s) => ({ ...s })) }));
}


function App() {
  // history — present is the full multi-floor state
  const [history, setHistory] = useState({
    past: [],
    present: { floors: makeInitialFloors(), activeId: 'fl-ground' },
    future: [],
  });
  const state = history.present;
  const floors = state.floors;
  const activeIdx = floors.findIndex((f) => f.id === state.activeId);
  const activeFloor = floors[activeIdx] || floors[0];
  const shapes = activeFloor.shapes;

  // generic state setter that records history; pass {touch: false} for view-only changes
  const commitState = useCallback((updater, { touch = true } = {}) => {
    setHistory((h) => {
      const next = typeof updater === 'function' ? updater(h.present) : updater;
      if (!touch) return { ...h, present: next };
      return { past: [...h.past, h.present].slice(-50), present: next, future: [] };
    });
  }, []);

  const setShapes = useCallback((updater) => {
    commitState((s) => {
      const cur = s.floors.find((f) => f.id === s.activeId);
      if (!cur) return s;
      const nextShapes = typeof updater === 'function' ? updater(cur.shapes) : updater;
      return { ...s, floors: s.floors.map((f) => f.id === s.activeId ? { ...f, shapes: nextShapes } : f) };
    });
  }, [commitState]);

  const setActiveFloor = useCallback((id) => {
    commitState((s) => ({ ...s, activeId: id }), { touch: false });
    setSelectedId(null);
  }, [commitState]);

  const addFloor = useCallback(() => {
    commitState((s) => {
      const maxLevel = Math.max(...s.floors.map((f) => f.level));
      const newId = 'fl-' + Math.random().toString(36).slice(2, 7);
      const name = `Level ${maxLevel + 2}`;
      const next = { id: newId, name, level: maxLevel + 1, shapes: [] };
      return { ...s, floors: [...s.floors, next], activeId: newId };
    });
  }, [commitState]);

  const renameFloor = useCallback((id, name) => {
    commitState((s) => ({ ...s, floors: s.floors.map((f) => f.id === id ? { ...f, name } : f) }));
  }, [commitState]);

  const deleteFloor = useCallback((id) => {
    commitState((s) => {
      if (s.floors.length <= 1) return s;
      const idx = s.floors.findIndex((f) => f.id === id);
      const remaining = s.floors.filter((f) => f.id !== id);
      const newActive = s.activeId === id ? remaining[Math.max(0, idx - 1)].id : s.activeId;
      return { ...s, floors: remaining, activeId: newActive };
    });
  }, [commitState]);

  const duplicateFloor = useCallback((id) => {
    commitState((s) => {
      const src = s.floors.find((f) => f.id === id);
      if (!src) return s;
      const newId = 'fl-' + Math.random().toString(36).slice(2, 7);
      // deep clone shapes with new ids
      const clonedShapes = src.shapes.map((sh) => ({ ...JSON.parse(JSON.stringify(sh)), id: window.uid() }));
      const next = { id: newId, name: src.name + ' copy', level: src.level + 0.5, shapes: clonedShapes };
      const ordered = [...s.floors, next].sort((a, b) => a.level - b.level);
      return { ...s, floors: ordered, activeId: newId };
    });
  }, [commitState]);

  const moveFloor = useCallback((id, dir) => {
    commitState((s) => {
      const idx = s.floors.findIndex((f) => f.id === id);
      const j = idx + dir;
      if (idx < 0 || j < 0 || j >= s.floors.length) return s;
      const arr = [...s.floors];
      [arr[idx], arr[j]] = [arr[j], arr[idx]];
      return { ...s, floors: arr };
    });
  }, [commitState]);

  const undo = useCallback(() => setHistory((h) => h.past.length ? { past: h.past.slice(0, -1), present: h.past[h.past.length - 1], future: [h.present, ...h.future] } : h), []);
  const redo = useCallback(() => setHistory((h) => h.future.length ? { past: [...h.past, h.present], present: h.future[0], future: h.future.slice(1) } : h), []);

  // tools
  const [tool, setTool] = useState('select');
  const [placingKind, setPlacingKind] = useState(null);
  const [selectedId, setSelectedId] = useState(null);
  const selected = shapes.find((s) => s.id === selectedId) || null;
  const updateSelected = (patch) => setShapes((arr) => arr.map((s) => s.id === selectedId ? { ...s, ...patch } : s));
  const deleteSelected = () => { setShapes((arr) => arr.filter((s) => s.id !== selectedId)); setSelectedId(null); };

  // clipboard for copy/paste (lives outside React state — no re-render needed)
  const clipboardRef = useRef(null);
  const offsetShape = (sh, dx, dy) => {
    const copy = JSON.parse(JSON.stringify(sh));
    copy.id = window.uid();
    if (copy.a && copy.b) {
      copy.a = { x: copy.a.x + dx, y: copy.a.y + dy };
      copy.b = { x: copy.b.x + dx, y: copy.b.y + dy };
    } else {
      if (typeof copy.x === 'number') copy.x += dx;
      if (typeof copy.y === 'number') copy.y += dy;
    }
    return copy;
  };
  const copySelected = () => { if (selected) clipboardRef.current = JSON.parse(JSON.stringify(selected)); };
  const pasteClipboard = () => {
    const src = clipboardRef.current;
    if (!src) return;
    const ng = offsetShape(src, 1, 1);
    setShapes((arr) => [...arr, ng]);
    setSelectedId(ng.id);
  };
  const duplicateSelected = () => {
    if (!selected) return;
    const ng = offsetShape(selected, 1, 1);
    setShapes((arr) => [...arr, ng]);
    setSelectedId(ng.id);
  };

  // view
  const [view, setView] = useState({ scale: 22, tx: 200, ty: 60 });

  // settings & tweaks
  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "vizStyle": "bauhaus",
    "paperStyle": "cream",
    "showGrid": true,
    "showDimensions": false,
    "gridSize": 1,
    "lineWeight": "medium",
    "accent": "yellow",
    "showGhost": true,
    "ghostMode": "below"
  }/*EDITMODE-END*/;
  const [t, setT] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  const [projectName, setProjectName] = useState('Untitled Plan');
  const [units, setUnits] = useState('ft');
  const gridSize = t.gridSize || 1;
  const setGridSize = (g) => setT('gridSize', g);

  // ghost layer: which other floor's shapes to show faded behind active
  const ghostShapes = useMemo(() => {
    if (!t.showGhost) return null;
    const mode = t.ghostMode || 'below';
    if (mode === 'none') return null;
    if (activeIdx < 0) return null;
    let target = null;
    if (mode === 'below' && activeIdx > 0) target = floors[activeIdx - 1];
    if (mode === 'above' && activeIdx < floors.length - 1) target = floors[activeIdx + 1];
    if (mode === 'all') {
      // merge all other floors
      const merged = [];
      floors.forEach((f, i) => { if (i !== activeIdx) merged.push(...f.shapes); });
      return merged.length ? merged : null;
    }
    return target ? target.shapes : null;
  }, [t.showGhost, t.ghostMode, floors, activeIdx]);

  // keyboard for undo/redo + floor switching
  useEffect(() => {
    const onKey = (e) => {
      if (e.target.tagName === 'INPUT') return;
      if ((e.ctrlKey || e.metaKey) && e.key === 'z') { e.preventDefault(); e.shiftKey ? redo() : undo(); }
      if ((e.ctrlKey || e.metaKey) && e.key === 'y') { e.preventDefault(); redo(); }
      if ((e.ctrlKey || e.metaKey) && e.key === 'c') { e.preventDefault(); copySelected(); }
      if ((e.ctrlKey || e.metaKey) && e.key === 'v') { e.preventDefault(); pasteClipboard(); }
      if ((e.ctrlKey || e.metaKey) && e.key === 'd') { e.preventDefault(); duplicateSelected(); }
      // Alt+1..9 jump to floor
      if (e.altKey && /^[1-9]$/.test(e.key)) {
        const i = +e.key - 1;
        if (floors[i]) { e.preventDefault(); setActiveFloor(floors[i].id); }
      }
      // PageUp/PageDown to change floor
      if (e.key === 'PageUp') { e.preventDefault(); const j = Math.min(floors.length - 1, activeIdx + 1); setActiveFloor(floors[j].id); }
      if (e.key === 'PageDown') { e.preventDefault(); const j = Math.max(0, activeIdx - 1); setActiveFloor(floors[j].id); }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [undo, redo, floors, activeIdx, setActiveFloor, selectedId]);

  // persist
  useEffect(() => {
    try { localStorage.setItem('floorcanvas:plan', JSON.stringify({ name: projectName, floors, activeId: state.activeId })); } catch {}
  }, [projectName, floors, state.activeId]);
  useEffect(() => {
    try {
      const saved = JSON.parse(localStorage.getItem('floorcanvas:plan') || 'null');
      if (saved && Array.isArray(saved.floors) && saved.floors.length) {
        const activeId = saved.activeId && saved.floors.find((f) => f.id === saved.activeId) ? saved.activeId : saved.floors[0].id;
        setHistory({ past: [], present: { floors: saved.floors, activeId }, future: [] });
        if (saved.name) setProjectName(saved.name);
      } else if (saved && saved.shapes && saved.shapes.length) {
        // migrate single-floor save
        const floor = { id: 'fl-ground', name: 'Ground', level: 0, shapes: saved.shapes };
        setHistory({ past: [], present: { floors: [floor], activeId: floor.id }, future: [] });
        if (saved.name) setProjectName(saved.name);
      }
    } catch {}
  }, []);

  // On small screens, auto-fit the active floor to the viewport once
  // after first paint so the user isn't dropped into a random corner
  // of the canvas.
  useEffect(() => {
    if (window.innerWidth > 768 && !window.matchMedia('(pointer: coarse)').matches) return;
    const stage = document.querySelector('.stage');
    if (!stage) return;
    const rect = stage.getBoundingClientRect();
    if (rect.width < 10 || rect.height < 10) return;
    // Compute bbox of current active floor's shapes (skip elevation drawings
    // far south of the plan so the floor plan dominates the framing).
    const active = floors.find((f) => f.id === state.activeId) || floors[0];
    const planShapes = active.shapes.filter((s) => {
      if (s.type === 'wall' || s.type === 'dimension') {
        return s.a.y < 32 && s.b.y < 32;
      }
      return (s.y || 0) < 32;
    });
    const shapesToUse = planShapes.length ? planShapes : active.shapes;
    if (!shapesToUse.length) return;
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    for (const sh of shapesToUse) {
      if (sh.type === 'wall' || sh.type === 'dimension') {
        minX = Math.min(minX, sh.a.x, sh.b.x); maxX = Math.max(maxX, sh.a.x, sh.b.x);
        minY = Math.min(minY, sh.a.y, sh.b.y); maxY = Math.max(maxY, sh.a.y, sh.b.y);
      } else {
        const w = sh.w ?? sh.width ?? 2, h = sh.h ?? 2;
        minX = Math.min(minX, sh.x); maxX = Math.max(maxX, sh.x + w);
        minY = Math.min(minY, sh.y); maxY = Math.max(maxY, sh.y + h);
      }
    }
    const pad = 3;
    const fitW = (maxX - minX) + pad * 2;
    const fitH = (maxY - minY) + pad * 2;
    const scale = Math.max(4, Math.min(120, Math.min(rect.width / fitW, rect.height / fitH)));
    setView({
      scale,
      tx: -(minX - pad) * scale + (rect.width  - fitW * scale) / 2,
      ty: -(minY - pad) * scale + (rect.height - fitH * scale) / 2,
    });
  // intentionally only on first mount
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // MCP bridge — re-attach on every render with fresh callbacks so the
  // bridge's RPC handler always sees the latest React state.
  useEffect(() => {
    if (!window.FCBridge) return;
    window.FCBridge.attach({
      getState: () => ({ name: projectName, floors, activeId: state.activeId }),
      getView: () => view,
      setView,
      setSelectedId,
      commitState,
      setHistory,
      setProjectName,
      setActiveFloor,
      undo,
      redo,
      setTweak: (k, v) => setT(k, v),
    });
  });

  // accent override
  useEffect(() => {
    const map = { yellow: '#f5d800', blue: '#0033a0', red: '#d72631' };
    document.documentElement.style.setProperty('--yellow', map[t.accent] || '#f5d800');
  }, [t.accent]);

  // visualization style override
  useEffect(() => {
    const styles = {
      bauhaus:    { '--paper': '#f1ebe1', '--paper-2': '#e7dfd2', '--paper-3': '#dccfba', '--paper-4': '#c8b99c', '--ink': '#0a0a0a', '--ink-4': '#4d4538', '--ink-5': '#6f6450', '--ink-6': '#8a7d65' },
      realistic:  { '--paper': '#f5e9d4', '--paper-2': '#ead9b8', '--paper-3': '#d9be8a', '--paper-4': '#b8966a', '--ink': '#1a1a1a', '--ink-4': '#3d3326', '--ink-5': '#5a4a35', '--ink-6': '#7d6b50' },
      blueprint:  { '--paper': '#0a2347', '--paper-2': '#0f2d5c', '--paper-3': '#163b73', '--paper-4': '#1d4a8c', '--ink': '#f0f4ff', '--ink-4': '#cfd9ee', '--ink-5': '#9eb1d4', '--ink-6': '#6b85b8' },
      sketch:     { '--paper': '#f7f1e3', '--paper-2': '#ede4c9', '--paper-3': '#e0d3ad', '--paper-4': '#cab98a', '--ink': '#2b2418', '--ink-4': '#5a4c33', '--ink-5': '#7c6a4a', '--ink-6': '#9a8866' },
      mono:       { '--paper': '#ffffff', '--paper-2': '#f4f4f4', '--paper-3': '#e8e8e8', '--paper-4': '#cccccc', '--ink': '#0a0a0a', '--ink-4': '#444444', '--ink-5': '#666666', '--ink-6': '#888888' },
      watercolor: { '--paper': '#faf6ef', '--paper-2': '#f0e6d9', '--paper-3': '#e1d2bd', '--paper-4': '#c9b59a', '--ink': '#3a3128', '--ink-4': '#6b5d4f', '--ink-5': '#8c7d6c', '--ink-6': '#a89888' },
      midcentury: { '--paper': '#e8dcc4', '--paper-2': '#d9c9a8', '--paper-3': '#c4b18a', '--paper-4': '#a89673', '--ink': '#2a1e10', '--ink-4': '#5c4525', '--ink-5': '#7a623a', '--ink-6': '#9a8254' },
    };
    const v = t.vizStyle || 'bauhaus';
    document.documentElement.setAttribute('data-viz-style', v);
    const s = styles[v] || styles.bauhaus;
    Object.entries(s).forEach(([k, val]) => document.documentElement.style.setProperty(k, val));
  }, [t.vizStyle]);

  const clearAll = () => { if (confirm(`Clear all shapes on ${activeFloor.name}?`)) { setShapes([]); setSelectedId(null); } };

  const exportSVG = () => {
    let xs = [], ys = [];
    shapes.forEach((s) => {
      if (s.type === 'wall' || s.type === 'dimension') { xs.push(s.a.x, s.b.x); ys.push(s.a.y, s.b.y); }
      else { xs.push(s.x); ys.push(s.y); xs.push(s.x + (s.w || s.width || 2)); ys.push(s.y + (s.h || 2)); }
    });
    if (!xs.length) return;
    const pad = 4;
    const minX = Math.min(...xs) - pad, maxX = Math.max(...xs) + pad;
    const minY = Math.min(...ys) - pad, maxY = Math.max(...ys) + pad;
    const stage = document.querySelector('.stage svg');
    if (!stage) return;
    const clone = stage.cloneNode(true);
    clone.setAttribute('viewBox', `${minX} ${minY} ${maxX - minX} ${maxY - minY}`);
    clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    const blob = new Blob([new XMLSerializer().serializeToString(clone)], { type: 'image/svg+xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `${projectName.replace(/\s+/g, '-').toLowerCase()}-${activeFloor.name.toLowerCase()}.svg`;
    document.body.appendChild(a); a.click(); document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const saveJSON = () => {
    const data = { name: projectName, floors, activeId: state.activeId };
    const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `${projectName.replace(/\s+/g, '-').toLowerCase()}.floorcanvas.json`;
    document.body.appendChild(a); a.click(); document.body.removeChild(a);
    URL.revokeObjectURL(url);
  };

  const loadJSON = () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'application/json,.json';
    input.onchange = (e) => {
      const file = e.target.files && e.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const data = JSON.parse(String(reader.result));
          if (!data || !Array.isArray(data.floors) || !data.floors.length) {
            alert('That file does not look like a FloorCanvas project.');
            return;
          }
          const activeId = data.activeId && data.floors.find((f) => f.id === data.activeId) ? data.activeId : data.floors[0].id;
          setHistory({ past: [], present: { floors: data.floors, activeId }, future: [] });
          if (data.name) setProjectName(data.name);
          setSelectedId(null);
        } catch (err) {
          alert('Could not parse JSON: ' + err.message);
        }
      };
      reader.readAsText(file);
    };
    input.click();
  };

  // tweaks UI
  const Tweaks = window.TweaksPanel;
  const TweakRadio = window.TweakRadio;
  const TweakToggle = window.TweakToggle;
  const TweakSelect = window.TweakSelect;
  const TweakSection = window.TweakSection;

  // mobile drawer state: '' | 'left' | 'right'
  const [drawer, setDrawer] = useState('');

  // Pan + zoom helpers shared by the touch nav cluster.
  const PAN_STEP = 80;     // screen pixels per tap
  const ZOOM_FACTOR = 1.25;
  const panBy = (dx, dy) => setView((v) => ({ ...v, tx: v.tx + dx, ty: v.ty + dy }));
  const zoomBy = (f) => setView((v) => {
    const stage = document.querySelector('.stage');
    const r = stage ? stage.getBoundingClientRect() : { width: 0, height: 0 };
    const cx = r.width / 2;
    const cy = r.height / 2;
    const wx = (cx - v.tx) / v.scale;
    const wy = (cy - v.ty) / v.scale;
    const ns = Math.max(4, Math.min(200, v.scale * f));
    return { scale: ns, tx: cx - wx * ns, ty: cy - wy * ns };
  });

  // "Drop to select" — exits placement mode and clears any selection so
  // the next tap is interpreted as a select-and-edit gesture.
  const dropToSelect = () => {
    setTool('select');
    setPlacingKind(null);
  };

  return (
    <div className="app has-floor-tabs" data-drawer={drawer}>
      <div className="drawer-backdrop" onClick={() => setDrawer('')} />
      <window.TopBar
        projectName={projectName} setProjectName={setProjectName}
        undo={undo} redo={redo} canUndo={history.past.length > 0} canRedo={history.future.length > 0}
        clearAll={clearAll} exportSVG={exportSVG} saveJSON={saveJSON} loadJSON={loadJSON}
        units={units} setUnits={setUnits} gridSize={gridSize} setGridSize={setGridSize}
        vizStyle={t.vizStyle} setVizStyle={(v) => setT('vizStyle', v)}
        activeFloorName={activeFloor.name}
        shapes={shapes}
        presentation={!!t.presentation} setPresentation={(v) => setT('presentation', v)}
      />
      <window.LeftRail tool={tool} setTool={setTool} setPlacingKind={setPlacingKind} />
      <div className="canvas">
        <window.FloorTabs
          floors={floors}
          activeId={state.activeId}
          setActiveFloor={setActiveFloor}
          addFloor={addFloor}
          renameFloor={renameFloor}
          deleteFloor={deleteFloor}
          duplicateFloor={duplicateFloor}
          moveFloor={moveFloor}
          showGhost={t.showGhost}
          ghostMode={t.ghostMode}
          setGhost={(v) => setT('showGhost', v)}
          setGhostMode={(v) => setT('ghostMode', v)}
        />
        <window.FloorCanvas
          shapes={shapes} setShapes={setShapes}
          ghostShapes={ghostShapes}
          tool={tool} setTool={setTool}
          placingKind={placingKind} setPlacingKind={setPlacingKind}
          selectedId={selectedId} setSelectedId={setSelectedId}
          view={view} setView={setView}
          gridSize={gridSize} showGrid={t.showGrid} showDimensions={t.showDimensions}
          paperStyle={t.paperStyle}
          presentation={!!t.presentation}
          projectName={projectName}
          activeFloorName={activeFloor.name}
        />
        {/* Selection bar + escape-to-select button — pinned to the top
            of the drawing area. Visible on every breakpoint. */}
        <div className="top-strip">
          <button className="top-select-btn" data-active={tool === 'select' && !placingKind}
            onClick={dropToSelect} aria-label="Select / cancel placement" title="Select / Esc">
            <svg viewBox="0 0 24 24"><path d="M5 3l14 7-6 2-2 6z"/></svg>
          </button>
          {selected && (
            <window.SelectionBar selected={selected} updateSelected={updateSelected} deleteSelected={deleteSelected} />
          )}
        </div>
        {/* Mobile floating action groups — hidden on desktop via CSS */}
        <div className="touch-fab-row">
          <div className="touch-fab-group is-left">
            <button className="touch-fab" data-active={drawer === 'left'}
              onClick={() => setDrawer(drawer === 'left' ? '' : 'left')}
              aria-label="Tools">
              <svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
            </button>
            <button className="touch-fab" onClick={undo} aria-label="Undo" disabled={!history.past.length}>
              <svg viewBox="0 0 24 24"><path d="M9 14L4 9l5-5M4 9h11a5 5 0 010 10h-4"/></svg>
            </button>
            <button className="touch-fab" onClick={redo} aria-label="Redo" disabled={!history.future.length}>
              <svg viewBox="0 0 24 24"><path d="M15 14l5-5-5-5M20 9H9a5 5 0 000 10h4"/></svg>
            </button>
            {selectedId && (
              <button className="touch-fab" onClick={deleteSelected} aria-label="Delete selection">
                <svg viewBox="0 0 24 24"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg>
              </button>
            )}
          </div>
          <div className="touch-fab-group is-right">
            <button className="touch-fab primary" data-active={drawer === 'right'}
              onClick={() => setDrawer(drawer === 'right' ? '' : 'right')}
              aria-label="Library">
              <svg viewBox="0 0 24 24"><path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z"/></svg>
            </button>
          </div>
          {/* Centered nav + zoom strip */}
          <div className="touch-nav">
            <button onClick={() => panBy(0, PAN_STEP)} aria-label="Pan up">
              <svg viewBox="0 0 24 24"><path d="M12 4v16M5 11l7-7 7 7"/></svg>
            </button>
            <button onClick={() => panBy(0, -PAN_STEP)} aria-label="Pan down">
              <svg viewBox="0 0 24 24"><path d="M12 4v16M5 13l7 7 7-7"/></svg>
            </button>
            <button onClick={() => panBy(PAN_STEP, 0)} aria-label="Pan left">
              <svg viewBox="0 0 24 24"><path d="M4 12h16M11 5l-7 7 7 7"/></svg>
            </button>
            <button onClick={() => panBy(-PAN_STEP, 0)} aria-label="Pan right">
              <svg viewBox="0 0 24 24"><path d="M4 12h16M13 5l7 7-7 7"/></svg>
            </button>
            <span className="sep" />
            <button onClick={() => zoomBy(1 / ZOOM_FACTOR)} aria-label="Zoom out">
              <svg viewBox="0 0 24 24"><path d="M5 12h14"/></svg>
            </button>
            <button onClick={() => zoomBy(ZOOM_FACTOR)} aria-label="Zoom in">
              <svg viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></svg>
            </button>
          </div>
        </div>
      </div>
      <window.LibraryPanel
        tool={tool}
        placingKind={placingKind} setPlacingKind={setPlacingKind} setTool={setTool}
        selected={selected} updateSelected={updateSelected} deleteSelected={deleteSelected}
      />
      <window.StatusBar shapes={shapes} tool={tool} gridSize={gridSize} units={units}
        floorName={activeFloor.name} floorCount={floors.length} />

      {Tweaks && (
        <Tweaks title="Tweaks">
          <TweakSection label="Visualization">
            <TweakSelect label="Style" value={t.vizStyle} options={[
              { value: 'bauhaus', label: 'Bauhaus' },
              { value: 'realistic', label: 'Realistic color' },
              { value: 'blueprint', label: 'Blueprint' },
              { value: 'sketch', label: 'Hand sketch' },
              { value: 'mono', label: 'Technical mono' },
              { value: 'watercolor', label: 'Watercolor' },
              { value: 'midcentury', label: 'Midcentury' },
            ]} onChange={(v) => setT('vizStyle', v)} />
          </TweakSection>
          <TweakSection label="Floors">
            <TweakToggle label="Show ghost layer" value={t.showGhost} onChange={(v) => setT('showGhost', v)} />
            <TweakSelect label="Ghost shows" value={t.ghostMode} options={[
              { value: 'below', label: 'Floor below' },
              { value: 'above', label: 'Floor above' },
              { value: 'all', label: 'All other floors' },
              { value: 'none', label: 'None' },
            ]} onChange={(v) => setT('ghostMode', v)} />
          </TweakSection>
          <TweakSection label="Paper">
            <TweakSelect label="Style" value={t.paperStyle} options={[
              { value: 'cream', label: 'Cream' },
              { value: 'white', label: 'White' },
              { value: 'blueprint', label: 'Blueprint' },
              { value: 'dots', label: 'Dots' },
            ]} onChange={(v) => setT('paperStyle', v)} />
            <TweakToggle label="Show grid" value={t.showGrid} onChange={(v) => setT('showGrid', v)} />
            <TweakToggle label="Auto-dimensions" value={t.showDimensions} onChange={(v) => setT('showDimensions', v)} />
          </TweakSection>
          <TweakSection label="Drafting">
            <TweakSelect label="Grid size" value={t.gridSize} options={[
              { value: 0.5, label: '6 inch' },
              { value: 1, label: '1 foot' },
              { value: 2, label: '2 foot' },
            ]} onChange={(v) => setT('gridSize', v)} />
            <TweakRadio label="Accent" value={t.accent} options={['yellow', 'blue', 'red']} onChange={(v) => setT('accent', v)} />
          </TweakSection>
        </Tweaks>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
