// FloorCanvas — AI Render (Nano Banana Pro)
// Captures the live floor plan, sends to Nano Banana Pro for a top-down 2D
// realistic rendering with landscaping. Falls back to a local compositor
// that wraps the actual plan screenshot in top-down vegetation.

const PROMPT = "Use an exact screenshot of the floor plan and create a realistic 2d top down rendering of the floor plan, add landscaping and exterior objects around the house as well to make it look more realistic";

const SITE_TYPES = [
  { id: 'suburban',  label: 'Suburban lot',    palette: ['#7fa362','#5d8a44','#3f6630'], path: '#c8b89c' },
  { id: 'coastal',   label: 'Coastal property', palette: ['#86a05e','#658a48','#476a30'], path: '#e9dcbf' },
  { id: 'forest',    label: 'Forest clearing',  palette: ['#4a7a3a','#2e5e22','#1c3e14'], path: '#7a6a52' },
  { id: 'desert',    label: 'Desert estate',    palette: ['#c8a878','#a8865a','#7e6240'], path: '#d8b88a' },
];

const DENSITY = [
  { id: 'sparse', label: 'Sparse',   k: 0.45 },
  { id: 'medium', label: 'Medium',   k: 1.0  },
  { id: 'lush',   label: 'Lush',     k: 1.7  },
];

const EXTRAS = [
  { id: 'pool',     label: 'Pool',         icon: '◗', def: true  },
  { id: 'driveway', label: 'Driveway + cars', icon: '▭', def: true },
  { id: 'patio',    label: 'Patio + furniture', icon: '▦', def: true },
  { id: 'path',     label: 'Garden paths',  icon: '⌒', def: true },
  { id: 'firepit',  label: 'Fire pit',      icon: '✦', def: false },
  { id: 'shed',     label: 'Garden shed',   icon: '⌂', def: false },
];

// ---- Capture current plan SVG → PNG dataURL --------------------------------

async function capturePlanPNG() {
  const stageSvg = document.querySelector('.stage svg');
  if (!stageSvg) return null;
  const clone = stageSvg.cloneNode(true);
  // Strip overlays/cursors
  clone.querySelectorAll('[data-overlay], .cursor-ind, .selection-handles').forEach((n) => n.remove());
  // Remove the grid background pattern — it dominates when rasterised
  clone.querySelectorAll('[fill*="grid"], rect[fill="url(#grid)"], rect[fill="url(#dot-grid)"]').forEach((n) => n.remove());
  clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
  // Inline computed CSS variables so colors render outside the document
  const rootStyles = getComputedStyle(document.documentElement);
  const vars = ['--paper','--paper-2','--paper-3','--paper-4','--ink','--ink-2','--ink-3','--ink-4','--ink-5','--ink-6','--red','--yellow','--blue','--blue-soft'];
  const cssVars = vars.map((v) => `${v}: ${rootStyles.getPropertyValue(v).trim()};`).join('');
  const styleTag = document.createElementNS('http://www.w3.org/2000/svg', 'style');
  styleTag.textContent = `:root { ${cssVars} } svg { background: ${rootStyles.getPropertyValue('--paper').trim()}; }`;
  clone.insertBefore(styleTag, clone.firstChild);
  // Force a paper background rect at the very back
  const bg = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
  const vb = (clone.getAttribute('viewBox') || '0 0 100 100').split(/\s+/).map(Number);
  bg.setAttribute('x', vb[0]); bg.setAttribute('y', vb[1]);
  bg.setAttribute('width', vb[2]); bg.setAttribute('height', vb[3]);
  bg.setAttribute('fill', rootStyles.getPropertyValue('--paper').trim() || '#f1ebe1');
  clone.insertBefore(bg, styleTag.nextSibling);
  const xml = new XMLSerializer().serializeToString(clone);
  const svgBlob = new Blob([xml], { type: 'image/svg+xml' });
  const url = URL.createObjectURL(svgBlob);
  const img = new Image();
  await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; img.src = url; });
  const w = 2000, h = 1400;
  const canvas = document.createElement('canvas');
  canvas.width = w; canvas.height = h;
  const ctx = canvas.getContext('2d');
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  ctx.fillStyle = '#f1ebe1';
  ctx.fillRect(0, 0, w, h);
  // fit svg into canvas preserving aspect
  const ar = img.naturalWidth / img.naturalHeight;
  let dw = w, dh = h;
  if (ar > w / h) { dh = w / ar; } else { dw = h * ar; }
  ctx.drawImage(img, (w - dw) / 2, (h - dh) / 2, dw, dh);
  URL.revokeObjectURL(url);
  return canvas.toDataURL('image/png');
}

// ---- Footprint extraction --------------------------------------------------

function extractFootprint(shapes) {
  const walls = shapes.filter((s) => s.type === 'wall');
  if (!walls.length) return null;
  let xs = [], ys = [];
  walls.forEach((w) => { xs.push(w.a.x, w.b.x); ys.push(w.a.y, w.b.y); });
  const minX = Math.min(...xs), maxX = Math.max(...xs);
  const minY = Math.min(...ys), maxY = Math.max(...ys);
  return { minX, maxX, minY, maxY, w: maxX - minX, h: maxY - minY };
}

// ---- Local fallback compositor ---------------------------------------------
// Produces a top-down realistic-style render around the actual plan screenshot.

async function localComposite({ planDataURL, footprint, shapes, site, density, extras }) {
  const W = 1600, H = 1000;
  const canvas = document.createElement('canvas');
  canvas.width = W; canvas.height = H;
  const ctx = canvas.getContext('2d');

  // 1) Grass base with multi-stop noise
  drawGrass(ctx, W, H, site);

  // 2) Compute plan placement (centered, with margin for landscaping)
  const planImg = new Image();
  await new Promise((r) => { planImg.onload = r; planImg.src = planDataURL; });

  const margin = 200;
  const maxW = W - margin * 2;
  const maxH = H - margin * 2;
  const planAR = planImg.naturalWidth / planImg.naturalHeight;
  let pw = maxW, ph = maxW / planAR;
  if (ph > maxH) { ph = maxH; pw = maxH * planAR; }
  const px = (W - pw) / 2;
  const py = (H - ph) / 2;

  // 3) Lot boundary — a slightly irregular lighter green rectangle behind plan
  const lotInset = 60;
  drawLot(ctx, px - lotInset, py - lotInset, pw + lotInset * 2, ph + lotInset * 2, site);

  // 4) Driveway / paths
  if (extras.includes('driveway')) drawDriveway(ctx, px, py, pw, ph, W, H, site);
  if (extras.includes('path')) drawPaths(ctx, px, py, pw, ph, site);

  // 5) Pool
  if (extras.includes('pool')) drawPool(ctx, px + pw * 0.78, py + ph + 30, Math.min(pw * 0.35, 180), 70);

  // 6) Patio with furniture
  if (extras.includes('patio')) drawPatio(ctx, px + pw * 0.18, py + ph + 40, 200, 130);

  // 7) Drop a soft shadow under the plan
  ctx.save();
  ctx.shadowColor = 'rgba(0,0,0,0.35)';
  ctx.shadowBlur = 28;
  ctx.shadowOffsetY = 10;
  ctx.fillStyle = '#fff';
  ctx.fillRect(px, py, pw, ph);
  ctx.restore();

  // 8) Draw the actual plan
  ctx.drawImage(planImg, px, py, pw, ph);

  // 9) Trees and shrubs around the perimeter (NOT over the plan)
  drawVegetation(ctx, W, H, px, py, pw, ph, site, density, extras);

  // 10) Extra landscape — fire pit, shed
  if (extras.includes('firepit')) drawFirepit(ctx, px - 90, py + ph * 0.6);
  if (extras.includes('shed')) drawShed(ctx, px + pw + 50, py + ph - 90);

  // 11) Vignette + grain
  drawVignette(ctx, W, H);
  drawGrain(ctx, W, H);

  return canvas.toDataURL('image/png');
}

function drawGrass(ctx, W, H, site) {
  const [g1, g2, g3] = site.palette;
  // base
  ctx.fillStyle = g2;
  ctx.fillRect(0, 0, W, H);
  // big soft blobs of variation
  for (let i = 0; i < 70; i++) {
    ctx.fillStyle = [g1, g3][i % 2];
    ctx.globalAlpha = 0.18 + Math.random() * 0.12;
    const x = Math.random() * W, y = Math.random() * H;
    const r = 80 + Math.random() * 180;
    const grd = ctx.createRadialGradient(x, y, 0, x, y, r);
    grd.addColorStop(0, [g1, g3][i % 2]);
    grd.addColorStop(1, 'transparent');
    ctx.fillStyle = grd;
    ctx.fillRect(x - r, y - r, r * 2, r * 2);
  }
  ctx.globalAlpha = 1;
  // grass speckle
  for (let i = 0; i < 4000; i++) {
    const x = Math.random() * W, y = Math.random() * H;
    ctx.fillStyle = Math.random() > 0.5 ? g1 : g3;
    ctx.globalAlpha = 0.15 + Math.random() * 0.25;
    ctx.fillRect(x, y, 1.5, 1.5);
  }
  ctx.globalAlpha = 1;
}

function drawLot(ctx, x, y, w, h, site) {
  // Manicured lawn around the house
  ctx.save();
  const grd = ctx.createRadialGradient(x + w / 2, y + h / 2, Math.min(w, h) * 0.2, x + w / 2, y + h / 2, Math.max(w, h) * 0.7);
  grd.addColorStop(0, lighten(site.palette[0], 0.15));
  grd.addColorStop(1, site.palette[0]);
  ctx.fillStyle = grd;
  // soft-edged rectangle
  roundRect(ctx, x, y, w, h, 24);
  ctx.fill();
  // stripe lawn lines
  ctx.globalAlpha = 0.12;
  ctx.fillStyle = lighten(site.palette[0], -0.2);
  for (let yy = y + 12; yy < y + h; yy += 16) {
    ctx.fillRect(x + 8, yy, w - 16, 1);
  }
  ctx.restore();
}

function drawDriveway(ctx, px, py, pw, ph, W, H, site) {
  // Driveway approaching from the bottom-right of the lot to the right edge of plan
  const startX = px + pw * 0.85;
  const startY = py + ph + 40;
  const endX = W - 40;
  const endY = H - 40;
  ctx.save();
  ctx.fillStyle = '#7a7268';
  ctx.beginPath();
  ctx.moveTo(startX - 60, startY);
  ctx.lineTo(startX + 60, startY);
  ctx.lineTo(endX, endY);
  ctx.lineTo(endX - 120, endY);
  ctx.closePath();
  ctx.fill();
  // texture pavers
  ctx.strokeStyle = 'rgba(0,0,0,0.18)';
  ctx.lineWidth = 1;
  for (let i = 0; i < 18; i++) {
    const t = i / 18;
    ctx.beginPath();
    ctx.moveTo(startX - 60 + t * 60, startY + t * (endY - startY));
    ctx.lineTo(startX + 60 - t * 60, startY + t * (endY - startY));
    ctx.stroke();
  }
  // Cars
  drawCar(ctx, (startX + endX) / 2 - 30, (startY + endY) / 2, '#b04030');
  drawCar(ctx, (startX + endX) / 2 + 40, (startY + endY) / 2 + 50, '#3a4a6a');
  ctx.restore();
}

function drawCar(ctx, x, y, color) {
  ctx.save();
  ctx.translate(x, y);
  ctx.rotate(Math.PI / 6);
  ctx.fillStyle = 'rgba(0,0,0,0.3)';
  ctx.fillRect(-26, -14, 56, 30);
  ctx.fillStyle = color;
  roundRect(ctx, -28, -16, 56, 30, 6);
  ctx.fill();
  ctx.fillStyle = 'rgba(255,255,255,0.4)';
  roundRect(ctx, -18, -10, 36, 18, 3);
  ctx.fill();
  ctx.fillStyle = 'rgba(0,0,0,0.25)';
  ctx.fillRect(-20, -1, 40, 2);
  ctx.restore();
}

function drawPaths(ctx, px, py, pw, ph, site) {
  ctx.save();
  ctx.fillStyle = site.path;
  // front walkway from plan front edge outward
  const wx = px + pw / 2 - 20;
  ctx.fillRect(wx, py + ph, 40, 80);
  // stepping stones
  ctx.fillStyle = '#d8c8a8';
  for (let i = 0; i < 6; i++) {
    ctx.beginPath();
    ctx.ellipse(px - 30 - i * 22, py + ph * 0.3 + i * 18, 14, 9, 0, 0, Math.PI * 2);
    ctx.fill();
  }
  ctx.restore();
}

function drawPool(ctx, cx, cy, w, h) {
  ctx.save();
  // coping
  ctx.fillStyle = '#e8e4d8';
  roundRect(ctx, cx - w / 2 - 8, cy - h / 2 - 8, w + 16, h + 16, 10);
  ctx.fill();
  // water
  const grd = ctx.createLinearGradient(cx, cy - h / 2, cx, cy + h / 2);
  grd.addColorStop(0, '#7ec5d8');
  grd.addColorStop(1, '#3d8db0');
  ctx.fillStyle = grd;
  roundRect(ctx, cx - w / 2, cy - h / 2, w, h, 6);
  ctx.fill();
  // highlights
  ctx.strokeStyle = 'rgba(255,255,255,0.35)';
  ctx.lineWidth = 1;
  for (let i = 0; i < 3; i++) {
    ctx.beginPath();
    ctx.moveTo(cx - w / 2 + 10, cy - h / 4 + i * 8);
    ctx.lineTo(cx + w / 2 - 10, cy - h / 4 + i * 8);
    ctx.stroke();
  }
  ctx.restore();
}

function drawPatio(ctx, cx, cy, w, h) {
  ctx.save();
  // deck
  ctx.fillStyle = '#a88860';
  roundRect(ctx, cx - w / 2, cy - h / 2, w, h, 4);
  ctx.fill();
  // planks
  ctx.strokeStyle = 'rgba(0,0,0,0.25)';
  ctx.lineWidth = 0.8;
  for (let i = 1; i < 8; i++) {
    ctx.beginPath();
    ctx.moveTo(cx - w / 2 + (i * w) / 8, cy - h / 2);
    ctx.lineTo(cx - w / 2 + (i * w) / 8, cy + h / 2);
    ctx.stroke();
  }
  // table
  ctx.fillStyle = 'rgba(0,0,0,0.2)';
  ctx.beginPath(); ctx.arc(cx, cy, 22, 0, Math.PI * 2); ctx.fill();
  ctx.fillStyle = '#d4c298';
  ctx.beginPath(); ctx.arc(cx, cy, 20, 0, Math.PI * 2); ctx.fill();
  // chairs
  [[-35,0],[35,0],[0,-30],[0,30]].forEach(([dx,dy]) => {
    ctx.fillStyle = '#5a4a36';
    ctx.beginPath(); ctx.arc(cx + dx, cy + dy, 10, 0, Math.PI * 2); ctx.fill();
    ctx.fillStyle = '#3a2e22';
    ctx.beginPath(); ctx.arc(cx + dx, cy + dy, 6, 0, Math.PI * 2); ctx.fill();
  });
  ctx.restore();
}

function drawFirepit(ctx, x, y) {
  ctx.save();
  // patio circle
  ctx.fillStyle = '#a8a098';
  ctx.beginPath(); ctx.arc(x, y, 50, 0, Math.PI * 2); ctx.fill();
  // pit
  ctx.fillStyle = '#2a1810';
  ctx.beginPath(); ctx.arc(x, y, 14, 0, Math.PI * 2); ctx.fill();
  ctx.fillStyle = '#ff8a2a';
  ctx.beginPath(); ctx.arc(x, y - 1, 8, 0, Math.PI * 2); ctx.fill();
  // chairs
  for (let i = 0; i < 5; i++) {
    const a = (i / 5) * Math.PI * 2;
    const cx = x + Math.cos(a) * 38;
    const cy = y + Math.sin(a) * 38;
    ctx.fillStyle = '#3a2818';
    ctx.beginPath(); ctx.arc(cx, cy, 7, 0, Math.PI * 2); ctx.fill();
  }
  ctx.restore();
}

function drawShed(ctx, x, y) {
  ctx.save();
  ctx.fillStyle = 'rgba(0,0,0,0.25)';
  ctx.fillRect(x + 4, y + 4, 80, 60);
  ctx.fillStyle = '#7a5e3a';
  ctx.fillRect(x, y, 80, 60);
  ctx.fillStyle = '#5a4628';
  ctx.fillRect(x, y, 80, 8);
  ctx.fillRect(x, y + 56, 80, 4);
  ctx.restore();
}

function drawVegetation(ctx, W, H, px, py, pw, ph, site, density, extras) {
  const k = DENSITY.find((d) => d.id === density).k;
  // Perimeter trees (large)
  const trees = [];
  // along top edge
  for (let x = 40; x < W - 40; x += 70 / k) {
    if (Math.random() > 0.85 / k) continue;
    const ty = 40 + Math.random() * 60;
    if (overlaps(x, ty, px - 30, py - 30, pw + 60, ph + 60)) continue;
    trees.push({ x, y: ty, r: 28 + Math.random() * 22 });
  }
  // along bottom edge
  for (let x = 40; x < W - 40; x += 80 / k) {
    if (Math.random() > 0.8 / k) continue;
    const ty = H - 60 - Math.random() * 60;
    if (overlaps(x, ty, px - 30, py - 30, pw + 60, ph + 60)) continue;
    trees.push({ x, y: ty, r: 30 + Math.random() * 26 });
  }
  // along sides
  for (let y = 80; y < H - 80; y += 80 / k) {
    if (Math.random() > 0.7 / k) {
      const tx = 30 + Math.random() * 50;
      if (!overlaps(tx, y, px - 30, py - 30, pw + 60, ph + 60)) trees.push({ x: tx, y, r: 28 + Math.random() * 24 });
    }
    if (Math.random() > 0.7 / k) {
      const tx = W - 30 - Math.random() * 50;
      if (!overlaps(tx, y, px - 30, py - 30, pw + 60, ph + 60)) trees.push({ x: tx, y, r: 28 + Math.random() * 24 });
    }
  }
  // Hedge inside lot border
  for (let i = 0; i < 50 * k; i++) {
    const onTop = Math.random() < 0.5;
    const x = px + Math.random() * pw;
    const y = onTop ? py - 18 + (Math.random() - 0.5) * 8 : py + ph + 18 + (Math.random() - 0.5) * 8;
    trees.push({ x, y, r: 12 + Math.random() * 6, kind: 'shrub' });
  }
  // Sort by y for depth
  trees.sort((a, b) => a.y - b.y);
  trees.forEach((t) => drawTree(ctx, t.x, t.y, t.r, t.kind === 'shrub', site));

  // Flower beds at corners
  for (let i = 0; i < 16 * k; i++) {
    const corner = i % 4;
    let x, y;
    if (corner === 0) { x = px - 20 + Math.random() * 40; y = py - 20 + Math.random() * 40; }
    else if (corner === 1) { x = px + pw - 20 + Math.random() * 40; y = py - 20 + Math.random() * 40; }
    else if (corner === 2) { x = px - 20 + Math.random() * 40; y = py + ph - 20 + Math.random() * 40; }
    else { x = px + pw - 20 + Math.random() * 40; y = py + ph - 20 + Math.random() * 40; }
    if (overlaps(x, y, px, py, pw, ph)) continue;
    const colors = ['#e8748a','#f0c050','#a878d0','#ffffff','#e85a3a'];
    ctx.fillStyle = colors[i % colors.length];
    ctx.beginPath(); ctx.arc(x, y, 3 + Math.random() * 2, 0, Math.PI * 2); ctx.fill();
  }
}

function drawTree(ctx, x, y, r, isShrub, site) {
  // shadow
  ctx.save();
  ctx.fillStyle = 'rgba(0,0,0,0.28)';
  ctx.beginPath();
  ctx.ellipse(x + r * 0.25, y + r * 0.25, r * 0.95, r * 0.7, 0, 0, Math.PI * 2);
  ctx.fill();
  ctx.restore();

  const dark = site.palette[2];
  const mid = site.palette[1];
  const light = site.palette[0];

  // canopy rosette — clusters of circles
  const clusters = isShrub ? 4 : 7;
  // base dark
  ctx.fillStyle = dark;
  ctx.beginPath();
  ctx.arc(x, y, r * 0.95, 0, Math.PI * 2);
  ctx.fill();
  for (let i = 0; i < clusters; i++) {
    const a = (i / clusters) * Math.PI * 2 + Math.random() * 0.4;
    const dx = Math.cos(a) * r * 0.4;
    const dy = Math.sin(a) * r * 0.4;
    ctx.fillStyle = i % 2 === 0 ? mid : darken(mid, 0.1);
    ctx.beginPath();
    ctx.arc(x + dx, y + dy, r * 0.5, 0, Math.PI * 2);
    ctx.fill();
  }
  // highlights
  for (let i = 0; i < clusters; i++) {
    const a = (i / clusters) * Math.PI * 2;
    const dx = Math.cos(a) * r * 0.25;
    const dy = Math.sin(a) * r * 0.25;
    ctx.fillStyle = light;
    ctx.globalAlpha = 0.7;
    ctx.beginPath();
    ctx.arc(x + dx - r * 0.1, y + dy - r * 0.15, r * 0.18, 0, Math.PI * 2);
    ctx.fill();
  }
  ctx.globalAlpha = 1;
}

function drawVignette(ctx, W, H) {
  const grd = ctx.createRadialGradient(W / 2, H / 2, Math.min(W, H) * 0.4, W / 2, H / 2, Math.max(W, H) * 0.75);
  grd.addColorStop(0, 'rgba(0,0,0,0)');
  grd.addColorStop(1, 'rgba(0,0,0,0.35)');
  ctx.fillStyle = grd;
  ctx.fillRect(0, 0, W, H);
}

function drawGrain(ctx, W, H) {
  ctx.save();
  ctx.globalAlpha = 0.06;
  for (let i = 0; i < 8000; i++) {
    ctx.fillStyle = Math.random() > 0.5 ? '#fff' : '#000';
    ctx.fillRect(Math.random() * W, Math.random() * H, 1, 1);
  }
  ctx.restore();
}

function overlaps(x, y, rx, ry, rw, rh) {
  return x >= rx && x <= rx + rw && y >= ry && y <= ry + rh;
}

function roundRect(ctx, x, y, w, h, r) {
  ctx.beginPath();
  ctx.moveTo(x + r, y);
  ctx.arcTo(x + w, y, x + w, y + h, r);
  ctx.arcTo(x + w, y + h, x, y + h, r);
  ctx.arcTo(x, y + h, x, y, r);
  ctx.arcTo(x, y, x + w, y, r);
  ctx.closePath();
}

function lighten(hex, amt) {
  const h = hex.replace('#',''); const r=parseInt(h.slice(0,2),16),g=parseInt(h.slice(2,4),16),b=parseInt(h.slice(4,6),16);
  const f = (v) => Math.max(0,Math.min(255,Math.round(v + (amt > 0 ? (255 - v) * amt : v * amt))));
  return `#${[r,g,b].map(f).map((v)=>v.toString(16).padStart(2,'0')).join('')}`;
}
function darken(hex, amt) { return lighten(hex, -amt); }

// ---- Modal -----------------------------------------------------------------

function RenderModal({ open, onClose, shapes, projectName, activeFloorName }) {
  const [site, setSite] = React.useState('suburban');
  const [density, setDensity] = React.useState('medium');
  const [extras, setExtras] = React.useState(EXTRAS.filter((e) => e.def).map((e) => e.id));
  const [rendering, setRendering] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [stage, setStage] = React.useState('idle'); // idle | capturing | calling | compositing
  const [result, setResult] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [previewURL, setPreviewURL] = React.useState(null);

  const siteObj = SITE_TYPES.find((s) => s.id === site);
  const footprint = React.useMemo(() => extractFootprint(shapes), [shapes]);

  // Live capture preview each time modal opens or shapes change
  React.useEffect(() => {
    if (!open) return;
    capturePlanPNG().then(setPreviewURL).catch(() => setPreviewURL(null));
  }, [open, shapes]);

  if (!open) return null;

  const toggleExtra = (id) => setExtras((e) => e.includes(id) ? e.filter((x) => x !== id) : [...e, id]);

  const runRender = async () => {
    setRendering(true); setResult(null); setError(null); setProgress(4);
    setStage('capturing');
    const progTimer = setInterval(() => setProgress((p) => Math.min(p + 2, 92)), 200);
    try {
      const planDataURL = await capturePlanPNG();
      if (!planDataURL) throw new Error('No plan to render — draw some walls first.');

      const apiKey = localStorage.getItem('floorcanvas:nbp-key');
      const modelOverride = localStorage.getItem('floorcanvas:nbp-model') || 'gemini-3-pro-image-preview';
      if (apiKey) {
        setStage('calling');
        // Nano Banana Pro multimodal call — model defaults to gemini-3-pro-image-preview
        const tryModels = [modelOverride, 'gemini-2.5-flash-image-preview', 'gemini-2.5-flash-image'];
        const base64 = planDataURL.split(',')[1];
        const body = {
          contents: [{ parts: [
            { inlineData: { mimeType: 'image/png', data: base64 } },
            { text: PROMPT },
          ]}],
          generationConfig: { responseModalities: ['TEXT', 'IMAGE'] },
        };
        let lastErr = null;
        for (const model of tryModels) {
          try {
            const r = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(body),
            });
            const data = await r.json();
            if (!r.ok) { lastErr = data?.error?.message || `HTTP ${r.status}`; continue; }
            const img = data?.candidates?.[0]?.content?.parts?.find((p) => p.inlineData)?.inlineData;
            if (img?.data) {
              setProgress(100); clearInterval(progTimer);
              setResult({ image: `data:${img.mimeType || 'image/png'};base64,${img.data}`, real: true, model });
              setRendering(false);
              return;
            }
            lastErr = data?.candidates?.[0]?.finishReason
              ? `${model}: finished with ${data.candidates[0].finishReason}, no image`
              : (data?.promptFeedback?.blockReason || `${model}: no image in response`);
          } catch (e) {
            lastErr = `${model}: ${e?.message || e}`;
          }
        }
        clearInterval(progTimer);
        setError(`Nano Banana Pro call failed — ${lastErr}. Check your key + model name (⚙). Falling back to local renderer.`);
        // continue to fallback below after a beat
        await new Promise((r) => setTimeout(r, 1500));
      }

      // Fallback: local compositor
      setStage('compositing');
      const out = await localComposite({ planDataURL, footprint, shapes, site: siteObj, density, extras });
      setProgress(100); clearInterval(progTimer);
      setResult({ image: out, real: false });
    } catch (e) {
      clearInterval(progTimer);
      setError(String(e?.message || e));
    } finally {
      setRendering(false);
      setStage('idle');
    }
  };

  const downloadResult = () => {
    if (!result?.image) return;
    const a = document.createElement('a');
    a.href = result.image; a.download = `${projectName.replace(/\s+/g,'-').toLowerCase()}-render.png`;
    document.body.appendChild(a); a.click(); a.remove();
  };

  const openSettings = () => {
    const curKey = localStorage.getItem('floorcanvas:nbp-key') || '';
    const k = window.prompt('Paste your Google Gemini API key (Nano Banana Pro).\nGet one at https://aistudio.google.com/apikey\n\nLeave blank to use the built-in local renderer.', curKey);
    if (k === null) return;
    if (k.trim()) localStorage.setItem('floorcanvas:nbp-key', k.trim());
    else { localStorage.removeItem('floorcanvas:nbp-key'); return; }
    const curModel = localStorage.getItem('floorcanvas:nbp-model') || 'gemini-3-pro-image-preview';
    const m = window.prompt('Model name (default = gemini-3-pro-image-preview / Nano Banana Pro).\n\nAlternatives:\n  gemini-2.5-flash-image-preview\n  gemini-2.5-flash-image', curModel);
    if (m && m.trim()) localStorage.setItem('floorcanvas:nbp-model', m.trim());
  };

  const hasKey = !!localStorage.getItem('floorcanvas:nbp-key');
  const stageLabel = { capturing: 'Capturing plan screenshot…', calling: 'Calling Nano Banana Pro…', compositing: 'Compositing landscaping…', idle: 'Rendering…' }[stage];

  return (
    <div className="nbp-backdrop" onClick={onClose}>
      <div className="nbp-modal" onClick={(e) => e.stopPropagation()}>
        <div className="nbp-head">
          <div className="nbp-head-l">
            <div className="nbp-badge">
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
                <path d="M12 2 L14 9 L21 12 L14 15 L12 22 L10 15 L3 12 L10 9 Z" fill="currentColor" />
              </svg>
              <span>Nano Banana Pro</span>
            </div>
            <h2>AI Render</h2>
            <p className="nbp-sub">{projectName} — {activeFloorName} · top-down 2D realistic</p>
          </div>
          <div className="nbp-head-r">
            <button className="nbp-icon-btn" onClick={openSettings} title={hasKey ? 'API key set (click to edit)' : 'Add API key'} style={hasKey ? { background: 'var(--yellow)' } : {}}>⚙</button>
            <button className="nbp-icon-btn" onClick={onClose}>✕</button>
          </div>
        </div>

        <div className="nbp-body">
          {/* LEFT: controls */}
          <div className="nbp-left">
            <div className="nbp-section">
              <div className="nbp-label">Source — current plan</div>
              <div className="nbp-thumb">
                {previewURL
                  ? <img src={previewURL} alt="plan preview" />
                  : <div className="nbp-thumb-empty">Draw walls first</div>}
              </div>
              <div className="nbp-prompt">
                <div className="nbp-prompt-l">Prompt</div>
                <div className="nbp-prompt-t">"{PROMPT}"</div>
              </div>
            </div>

            <div className="nbp-section">
              <div className="nbp-label">Site context</div>
              <div className="nbp-pills">
                {SITE_TYPES.map((s) => (
                  <button key={s.id} className={`nbp-pill ${site === s.id ? 'active' : ''}`} onClick={() => setSite(s.id)}>{s.label}</button>
                ))}
              </div>
            </div>

            <div className="nbp-section">
              <div className="nbp-label">Landscaping density</div>
              <div className="nbp-pills">
                {DENSITY.map((d) => (
                  <button key={d.id} className={`nbp-pill ${density === d.id ? 'active' : ''}`} onClick={() => setDensity(d.id)}>{d.label}</button>
                ))}
              </div>
            </div>

            <div className="nbp-section">
              <div className="nbp-label">Exterior objects</div>
              <div className="nbp-feats">
                {EXTRAS.map((f) => (
                  <button key={f.id} className={`nbp-feat ${extras.includes(f.id) ? 'active' : ''}`} onClick={() => toggleExtra(f.id)}>
                    <span className="ic">{f.icon}</span>
                    <span>{f.label}</span>
                  </button>
                ))}
              </div>
            </div>

            <div className="nbp-section">
              <button className="nbp-render-btn" onClick={runRender} disabled={rendering || !footprint}>
                {rendering ? `${stageLabel} ${progress}%` : (result ? 'Re-render' : 'Generate render')}
              </button>
              {rendering && (
                <div className="nbp-prog"><div className="nbp-prog-fill" style={{ width: `${progress}%` }} /></div>
              )}
              {!footprint && <div className="nbp-warn">Draw walls before rendering.</div>}
              {!hasKey && footprint && <div className="nbp-note">No API key — using built-in renderer. Click ⚙ to add a Gemini key for true Nano Banana Pro output.</div>}
            </div>
          </div>

          {/* RIGHT: result */}
          <div className="nbp-right">
            <div className="nbp-frame">
              {!result && !rendering && (
                <div className="nbp-empty">
                  <div className="nbp-empty-ic">
                    <svg viewBox="0 0 60 60" width="60" height="60" fill="none" stroke="currentColor" strokeWidth="1.5">
                      <rect x="10" y="10" width="40" height="40" />
                      <circle cx="22" cy="22" r="3" />
                      <path d="M10 40 L24 28 L34 38 L50 22" />
                    </svg>
                  </div>
                  <div className="nbp-empty-t">No render yet</div>
                  <div className="nbp-empty-s">Tune the site, then generate.</div>
                </div>
              )}
              {rendering && (
                <div className="nbp-loading">
                  <div className="nbp-spinner" />
                  <div className="nbp-loading-t">{stageLabel}</div>
                  <div className="nbp-loading-s">Capture → Prompt → Image → Output</div>
                </div>
              )}
              {result && (
                <img src={result.image} alt="render" style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block', background: '#0a0a0a' }} />
              )}
              {error && <div className="nbp-err">{error}</div>}
            </div>

            {result && (
              <div className="nbp-meta">
                <div className="nbp-caption">
                  {result.real
                    ? <>Rendered by Nano Banana Pro (gemini-2.5-flash-image-preview) — top-down 2D photoreal with AI-generated landscaping and exterior objects.</>
                    : <>Local top-down composite — actual plan screenshot wrapped with vector landscaping. Add a Gemini API key (⚙) for true Nano Banana Pro photoreal output.</>}
                </div>
                <div className="nbp-tags">
                  <span className="nbp-tag">{siteObj.label}</span>
                  <span className="nbp-tag">{DENSITY.find((d) => d.id === density).label} density</span>
                  {extras.map((id) => <span key={id} className="nbp-tag">{EXTRAS.find((f) => f.id === id).label}</span>)}
                </div>
              </div>
            )}

            {result && (
              <div className="nbp-actions">
                <button className="nbp-act" onClick={() => setResult(null)}>Discard</button>
                <button className="nbp-act primary" onClick={downloadResult}>Download PNG</button>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

// ---- Topbar button ---------------------------------------------------------

function RenderButton({ shapes, projectName, activeFloorName }) {
  const [open, setOpen] = React.useState(false);
  return (
    <>
      <button className="tb-btn nbp-trigger" onClick={() => setOpen(true)} title="AI render (Nano Banana Pro)">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style={{ marginRight: 5, verticalAlign: -2 }}>
          <path d="M12 2 L14 9 L21 12 L14 15 L12 22 L10 15 L3 12 L10 9 Z" />
        </svg>
        render
      </button>
      <RenderModal open={open} onClose={() => setOpen(false)} shapes={shapes} projectName={projectName} activeFloorName={activeFloorName} />
    </>
  );
}

Object.assign(window, { RenderButton, RenderModal });
