// ScoreView: Rendert Demo-SVG oder PDF und legt Annotations-Layer darüber

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

function ScoreView({ piece, annotations, tool, toolOptions, onAddPin, onAddStroke, onAddStamp, onPinClick, onMovePin, onMoveStamp, onResizeStamp, selectedPinId, selectedStampId, onStampClick, onPagesChanged, onAddBow, onUpdateBow, onBowClick, selectedBowId, zoom = 1 }) {
  const containerRef = useRef(null);
  const [pages, setPages] = useState([]);
  const [loading, setLoading] = useState(false);
  const [hint, setHint] = useState('');

  // Lade Stück
  useEffect(() => {
    if (!piece) {
      setPages([]);
      return;
    }
    if (piece.kind === 'demo') {
      // Lade Demo-SVG
      setLoading(true);
      fetch('demo-score.svg')
        .then(r => r.text())
        .then(svgText => {
          const match = svgText.match(/viewBox="([^"]+)"/);
          let w = 1200, h = 1600;
          if (match) {
            const parts = match[1].split(/\s+/).map(Number);
            w = parts[2]; h = parts[3];
          }
          setPages([{ kind: 'svg', svg: svgText, width: w, height: h }]);
          setLoading(false);
          if (onPagesChanged) onPagesChanged(1);
        });
      return;
    }
    if (piece.kind === 'pdf' && piece.pdfFilename) {
      // PDF vom Server laden
      setLoading(true);
      loadPdfFromUrl('/api/uploads/' + piece.pdfFilename).then(loadedPages => {
        setPages(loadedPages);
        setLoading(false);
        if (onPagesChanged) onPagesChanged(loadedPages.length);
      }).catch(err => {
        console.error('PDF load failed', err);
        setLoading(false);
      });
    }
  }, [piece?.id]);

  async function loadPdfFromUrl(url) {
    if (!window.pdfjsLib) {
      throw new Error('pdfjsLib not loaded');
    }
    const pdfjs = window.pdfjsLib;
    pdfjs.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@3.11.174/build/pdf.worker.min.js';
    const pdf = await pdfjs.getDocument(url).promise;
    const out = [];
    for (let i = 1; i <= pdf.numPages; i++) {
      const page = await pdf.getPage(i);
      const viewport = page.getViewport({ scale: 1.5 });
      out.push({
        kind: 'pdf',
        page,
        width: viewport.width,
        height: viewport.height,
        scale: 1.5,
      });
    }
    return out;
  }

  // Render PDF pages auf canvas elements
  useEffect(() => {
    pages.forEach((p, idx) => {
      if (p.kind === 'pdf') {
        const canvas = document.querySelector(`[data-pdf-canvas="${idx}"]`);
        if (canvas && !canvas.dataset.rendered) {
          canvas.dataset.rendered = '1';
          canvas.width = p.width;
          canvas.height = p.height;
          const ctx = canvas.getContext('2d');
          p.page.render({ canvasContext: ctx, viewport: p.page.getViewport({ scale: p.scale }) });
        }
      }
    });
  }, [pages]);

  if (!piece) {
    return null;
  }

  if (loading) {
    return (
      <div className="canvas-empty">
        <div className="spinner"></div>
        <p>Lade Noten…</p>
      </div>
    );
  }

  const showHint = (msg) => {
    setHint(msg);
    setTimeout(() => setHint(''), 2200);
  };

  return (
    <>
      {pages.map((p, pageIndex) => (
        <ScorePage
          key={pageIndex}
          page={p}
          pageIndex={pageIndex}
          tool={tool}
          toolOptions={toolOptions}
          annotations={annotations}
          onAddPin={(x, y) => { onAddPin(pageIndex, x, y); }}
          onAddStroke={(stroke) => { onAddStroke(pageIndex, stroke); }}
          onAddStamp={(x, y) => { if (onAddStamp) onAddStamp(pageIndex, x, y); }}
          onPinClick={onPinClick}
          onMovePin={onMovePin}
          onStampClick={onStampClick}
          onMoveStamp={onMoveStamp}
          onResizeStamp={onResizeStamp}
          selectedPinId={selectedPinId}
          selectedStampId={selectedStampId}
          onAddBow={onAddBow}
          onUpdateBow={onUpdateBow}
          onBowClick={onBowClick}
          selectedBowId={selectedBowId}
          showHint={showHint}
          zoom={zoom}
        />
      ))}
      {hint && <div className="hint">{hint}</div>}
    </>
  );
}

function ScorePage({ page, pageIndex, tool, toolOptions, annotations, onAddPin, onAddStroke, onAddStamp, onPinClick, onMovePin, onStampClick, onMoveStamp, onResizeStamp, selectedPinId, selectedStampId, onAddBow, onUpdateBow, onBowClick, selectedBowId, showHint, zoom = 1 }) {
  const wrapRef = useRef(null);
  const overlayRef = useRef(null);
  const [drawing, setDrawing] = useState(null);
  const [draggingPin, setDraggingPin] = useState(null);
  const [draggingStamp, setDraggingStamp] = useState(null);
  const [resizingStamp, setResizingStamp] = useState(null);
  const [bowDraft, setBowDraft] = useState(null); // { x1, y1, x2, y2 } while drawing
  const [draggingBowEnd, setDraggingBowEnd] = useState(null); // { id, end: 'start'|'end' }

  const pageStamps = (annotations.stamps || []).filter(s => s.pageIndex === pageIndex);
  const pageBows = (annotations.bows || []).filter(b => b.pageIndex === pageIndex);

  // Bow helper: compute control point Y for quadratic bezier
  const bowCpY = (bow) => {
    const dist = Math.hypot(bow.x2 - bow.x1, bow.y2 - bow.y1);
    const curve = bow.curve !== undefined ? bow.curve : 0.15;
    return Math.min(bow.y1, bow.y2) - dist * curve;
  };
  const bowCpYPx = (bow, dw, dh) => {
    const bx1 = bow.x1 * dw, by1 = bow.y1 * dh, bx2 = bow.x2 * dw, by2 = bow.y2 * dh;
    const dist = Math.hypot(bx2 - bx1, by2 - by1);
    const curve = bow.curve !== undefined ? bow.curve : 0.15;
    return Math.min(by1, by2) - dist * curve;
  };

  const pageStrokes = (annotations.strokes || []).filter(s => s.pageIndex === pageIndex);
  const pagePins = (annotations.pins || []).filter(p => p.pageIndex === pageIndex);

  const baseWidth = Math.min(page.width, 1000);
  const displayWidth = Math.round(baseWidth * zoom);
  const displayHeight = displayWidth * (page.height / page.width);

  const getRel = (e) => {
    const rect = overlayRef.current.getBoundingClientRect();
    return {
      x: (e.clientX - rect.left) / rect.width,
      y: (e.clientY - rect.top) / rect.height,
    };
  };

  const handleClick = (e) => {
    if (drawing) return;
    const { x, y } = getRel(e);
    if (tool === 'pin') {
      onAddPin(x, y);
    } else if (tool.startsWith('stamp-')) {
      onAddStamp(x, y);
    }
  };

  const handlePointerDown = (e) => {
    if (tool === 'bow') {
      e.preventDefault();
      overlayRef.current.setPointerCapture(e.pointerId);
      const { x, y } = getRel(e);
      setBowDraft({ x1: x, y1: y, x2: x, y2: y });
      return;
    }
    if (tool !== 'pen' && tool !== 'highlight' && tool !== 'eraser') return;
    e.preventDefault();
    overlayRef.current.setPointerCapture(e.pointerId);
    const { x, y } = getRel(e);
    if (tool === 'eraser') {
      eraseAt(x, y);
      setDrawing({ erasing: true });
      return;
    }
    setDrawing({
      tool,
      color: tool === 'highlight' ? toolOptions.highlightColor : toolOptions.penColor,
      width: tool === 'highlight' ? toolOptions.highlightSize : toolOptions.penSize,
      opacity: tool === 'highlight' ? 0.4 : 1,
      points: [{ x, y }],
    });
  };

  const handlePointerMove = (e) => {
    if (bowDraft) {
      const { x, y } = getRel(e);
      setBowDraft(d => ({ ...d, x2: x, y2: y }));
      return;
    }
    if (!drawing) return;
    const { x, y } = getRel(e);
    if (drawing.erasing) {
      eraseAt(x, y);
      return;
    }
    setDrawing(d => ({ ...d, points: [...d.points, { x, y }] }));
  };

  const handlePointerUp = (e) => {
    if (bowDraft) {
      const dx = Math.abs(bowDraft.x2 - bowDraft.x1);
      if (dx > 0.015) {
        const bow = {
          id: uid(),
          pageIndex,
          x1: bowDraft.x1, y1: bowDraft.y1,
          x2: bowDraft.x2, y2: bowDraft.y2,
          curve: 0.15,
          color: toolOptions.penColor,
          createdAt: Date.now(),
        };
        if (onAddBow) onAddBow(bow);
      }
      setBowDraft(null);
      return;
    }
    if (!drawing) return;
    if (!drawing.erasing && drawing.points.length > 1) {
      onAddStroke({
        id: uid(),
        tool: drawing.tool,
        color: drawing.color,
        width: drawing.width,
        opacity: drawing.opacity,
        points: drawing.points,
      });
    }
    setDrawing(null);
  };

  const eraseAt = (x, y) => {
    const threshold = 0.012;
    const toRemove = [];
    pageStrokes.forEach(s => {
      const hit = s.points.some(p => Math.hypot(p.x - x, p.y - y) < threshold);
      if (hit) toRemove.push(s.id);
    });
    if (toRemove.length) {
      window.dispatchEvent(new CustomEvent('erase-strokes', { detail: { ids: toRemove } }));
    }
    // Erase bows
    const bowsToRemove = [];
    pageBows.forEach(bow => {
      const cpYn = bowCpY(bow);
      const midXn = (bow.x1 + bow.x2) / 2;
      for (let t = 0; t <= 1; t += 0.05) {
        const bx = (1 - t) * (1 - t) * bow.x1 + 2 * (1 - t) * t * midXn + t * t * bow.x2;
        const by = (1 - t) * (1 - t) * bow.y1 + 2 * (1 - t) * t * cpYn + t * t * bow.y2;
        if (Math.hypot(bx - x, by - y) < threshold) {
          bowsToRemove.push(bow.id);
          break;
        }
      }
    });
    if (bowsToRemove.length) {
      window.dispatchEvent(new CustomEvent('erase-bows', { detail: { ids: bowsToRemove } }));
    }
  };

  const isStampTool = tool.startsWith('stamp-');
  const toolActive = tool !== 'select';
  const cursorStyle = tool === 'pen' ? 'crosshair' :
                       tool === 'highlight' ? 'crosshair' :
                       tool === 'eraser' ? 'cell' :
                       tool === 'pin' ? 'copy' :
                       tool === 'bow' ? 'crosshair' :
                       isStampTool ? 'copy' : 'default';

  const pointsToPath = (points) => {
    if (points.length < 2) return '';
    let d = `M ${points[0].x * displayWidth} ${points[0].y * displayHeight}`;
    for (let i = 1; i < points.length; i++) {
      d += ` L ${points[i].x * displayWidth} ${points[i].y * displayHeight}`;
    }
    return d;
  };

  return (
    <div
      ref={wrapRef}
      className="page-wrap"
      style={{ width: displayWidth, height: displayHeight }}
    >
      <div className="page-label">Seite {pageIndex + 1}</div>

      {page.kind === 'svg' ? (
        <div
          style={{ width: '100%', height: '100%' }}
          dangerouslySetInnerHTML={{ __html: page.svg.replace(/width="\d+"/, `width="${displayWidth}"`).replace(/height="\d+"/, `height="${displayHeight}"`) }}
        />
      ) : (
        <canvas
          data-pdf-canvas={pageIndex}
          style={{ width: '100%', height: '100%', display: 'block' }}
        />
      )}

      <svg
        style={{
          position: 'absolute', top: 0, left: 0,
          width: '100%', height: '100%',
          pointerEvents: 'none',
        }}
        viewBox={`0 0 ${displayWidth} ${displayHeight}`}
      >
        {pageStrokes.map(s => (
          <path
            key={s.id}
            d={pointsToPath(s.points)}
            stroke={s.color}
            strokeWidth={s.width}
            strokeLinecap="round"
            strokeLinejoin="round"
            fill="none"
            opacity={s.opacity}
          />
        ))}
        {drawing && !drawing.erasing && drawing.points.length > 0 && (
          <path
            d={pointsToPath(drawing.points)}
            stroke={drawing.color}
            strokeWidth={drawing.width}
            strokeLinecap="round"
            strokeLinejoin="round"
            fill="none"
            opacity={drawing.opacity}
          />
        )}
        {/* Rendered bows */}
        {pageBows.map(bow => {
          const bx1 = bow.x1 * displayWidth, by1 = bow.y1 * displayHeight;
          const bx2 = bow.x2 * displayWidth, by2 = bow.y2 * displayHeight;
          const midX = (bx1 + bx2) / 2;
          const cpY = bowCpYPx(bow, displayWidth, displayHeight);
          const isSelected = bow.id === selectedBowId;
          return (
            <g key={bow.id}>
              <path
                d={`M ${bx1} ${by1} Q ${midX} ${cpY} ${bx2} ${by2}`}
                stroke={bow.color || '#1c1917'}
                strokeWidth={isSelected ? 2.5 : 2}
                strokeLinecap="round"
                fill="none"
                opacity={0.85}
                style={{ pointerEvents: 'stroke', cursor: 'pointer' }}
                onClick={(e) => { e.stopPropagation(); if (onBowClick) onBowClick(bow.id); }}
              />
              {isSelected && (
                <path
                  d={`M ${bx1} ${by1} Q ${midX} ${cpY} ${bx2} ${by2}`}
                  stroke="var(--v2)"
                  strokeWidth={1}
                  strokeDasharray="4 3"
                  fill="none"
                  style={{ pointerEvents: 'none' }}
                />
              )}
            </g>
          );
        })}
        {/* Bow draft while drawing */}
        {bowDraft && (() => {
          const draft = { ...bowDraft, curve: 0.15 };
          const bx1 = draft.x1 * displayWidth, by1 = draft.y1 * displayHeight;
          const bx2 = draft.x2 * displayWidth, by2 = draft.y2 * displayHeight;
          const midX = (bx1 + bx2) / 2;
          const cpY = bowCpYPx(draft, displayWidth, displayHeight);
          return (
            <path
              d={`M ${bx1} ${by1} Q ${midX} ${cpY} ${bx2} ${by2}`}
              stroke={toolOptions.penColor}
              strokeWidth={2}
              strokeLinecap="round"
              fill="none"
              opacity={0.6}
              strokeDasharray="4 3"
            />
          );
        })()}
      </svg>

      {/* Bow handles (endpoints + curve) */}
      {pageBows.filter(b => b.id === selectedBowId && tool === 'select').map(bow => {
        const cpYPx = bowCpYPx(bow, displayWidth, displayHeight);
        const midXPct = ((bow.x1 + bow.x2) / 2) * 100;
        const cpYPct = (cpYPx / displayHeight) * 100;
        return (
          <React.Fragment key={'bh-' + bow.id}>
            {['start', 'end'].map(end => {
              const bx = end === 'start' ? bow.x1 : bow.x2;
              const by = end === 'start' ? bow.y1 : bow.y2;
              return (
                <div
                  key={end}
                  className="bow-handle"
                  style={{ left: `${bx * 100}%`, top: `${by * 100}%` }}
                  onPointerDown={(e) => {
                    e.stopPropagation();
                    e.preventDefault();
                    e.currentTarget.setPointerCapture(e.pointerId);
                    setDraggingBowEnd({ id: bow.id, end });
                  }}
                  onPointerMove={(e) => {
                    if (!draggingBowEnd || draggingBowEnd.id !== bow.id || draggingBowEnd.end !== end) return;
                    const rect = wrapRef.current.getBoundingClientRect();
                    const nx = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
                    const ny = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
                    const updated = draggingBowEnd.end === 'start'
                      ? { ...bow, x1: nx, y1: ny }
                      : { ...bow, x2: nx, y2: ny };
                    if (onUpdateBow) onUpdateBow(updated);
                  }}
                  onPointerUp={() => setDraggingBowEnd(null)}
                  onPointerCancel={() => setDraggingBowEnd(null)}
                />
              );
            })}
            {/* Curve handle at apex */}
            <div
              className="bow-handle bow-handle-curve"
              style={{ left: `${midXPct}%`, top: `${cpYPct}%` }}
              onPointerDown={(e) => {
                e.stopPropagation();
                e.preventDefault();
                e.currentTarget.setPointerCapture(e.pointerId);
                setDraggingBowEnd({ id: bow.id, end: 'curve' });
              }}
              onPointerMove={(e) => {
                if (!draggingBowEnd || draggingBowEnd.id !== bow.id || draggingBowEnd.end !== 'curve') return;
                const rect = wrapRef.current.getBoundingClientRect();
                const mouseY = (e.clientY - rect.top) / rect.height;
                const minY = Math.min(bow.y1, bow.y2);
                const dist = Math.hypot(
                  (bow.x2 - bow.x1) * displayWidth,
                  (bow.y2 - bow.y1) * displayHeight
                );
                // curve = (minY - mouseY) * displayHeight / dist
                const newCurve = Math.max(0.02, Math.min(0.8, (minY - mouseY) * displayHeight / dist));
                if (onUpdateBow) onUpdateBow({ ...bow, curve: newCurve });
              }}
              onPointerUp={() => setDraggingBowEnd(null)}
              onPointerCancel={() => setDraggingBowEnd(null)}
            />
          </React.Fragment>
        );
      })}

      {pagePins.map((pin) => {
        const preview = pin.text ? pin.text.split(/\s+/).slice(0, 5).join(' ') : '';
        const isDragging = draggingPin === pin.id;
        const globalIdx = (annotations.pins || []).findIndex(p => p.id === pin.id);
        return (
          <div
            key={pin.id}
            className={`pin-group ${pin.id === selectedPinId ? 'selected' : ''} ${isDragging ? 'dragging' : ''}`}
            style={{
              left: `${pin.x * 100}%`,
              top: `${pin.y * 100}%`,
            }}
            onClick={(e) => { e.stopPropagation(); if (!isDragging) onPinClick(pin.id); }}
            onPointerDown={(e) => {
              if (tool !== 'select') return;
              e.stopPropagation();
              e.preventDefault();
              e.currentTarget.setPointerCapture(e.pointerId);
              setDraggingPin(pin.id);
            }}
            onPointerMove={(e) => {
              if (draggingPin !== pin.id) return;
              const rect = wrapRef.current.getBoundingClientRect();
              const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
              const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
              if (onMovePin) onMovePin(pin.id, x, y);
            }}
            onPointerUp={(e) => {
              if (draggingPin !== pin.id) return;
              setDraggingPin(null);
            }}
            onPointerCancel={() => setDraggingPin(null)}
          >
            <div className="pin-marker" style={{ background: window.phaseColor(pin.phase) }}>
              <span>{globalIdx + 1}</span>
            </div>
            {preview && (
              <div className="pin-label">
                <span className="pin-label-phase" style={{ color: window.phaseColor(pin.phase) }}>{pin.phase}</span>
                {preview}{pin.text.split(/\s+/).length > 5 ? '…' : ''}
              </div>
            )}
          </div>
        );
      })}

      {/* Stamp-Marker */}
      {pageStamps.map(stamp => {
        const isDragging = draggingStamp === stamp.id;
        const isResizing = resizingStamp && resizingStamp.id === stamp.id;
        const iconName = stamp.type.replace('stamp-', '');
        const scale = stamp.scale || 1;
        const baseSize = 28;
        const iconSize = Math.round(baseSize * scale);
        const isSelected = stamp.id === selectedStampId;
        const stampColor = stamp.color || '#1c1917';
        const isBeat = stamp.type.startsWith('stamp-beat-');
        return (
          <div
            key={stamp.id}
            className={`stamp-marker ${isSelected ? 'selected' : ''} ${isDragging ? 'dragging' : ''} ${isBeat ? 'stamp-beat' : ''}`}
            style={{
              left: `${stamp.x * 100}%`,
              color: stampColor,
              ...(isBeat ? { borderColor: stampColor } : {}),
              top: `${stamp.y * 100}%`,
            }}
            onClick={(e) => { e.stopPropagation(); if (onStampClick) onStampClick(stamp.id); }}
            onPointerDown={(e) => {
              if (tool !== 'select') return;
              e.stopPropagation();
              e.preventDefault();
              e.currentTarget.setPointerCapture(e.pointerId);
              setDraggingStamp(stamp.id);
            }}
            onPointerMove={(e) => {
              if (draggingStamp !== stamp.id) return;
              const rect = wrapRef.current.getBoundingClientRect();
              const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
              const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
              if (onMoveStamp) onMoveStamp(stamp.id, x, y);
            }}
            onPointerUp={() => { if (draggingStamp === stamp.id) setDraggingStamp(null); }}
            onPointerCancel={() => setDraggingStamp(null)}
          >
            <Icon name={iconName} size={iconSize} stroke={Math.max(1.5, 2.5 * scale)} />
            {isSelected && tool === 'select' && (
              <div
                className="stamp-resize-handle"
                onPointerDown={(e) => {
                  e.stopPropagation();
                  e.preventDefault();
                  e.currentTarget.setPointerCapture(e.pointerId);
                  setResizingStamp({ id: stamp.id, startY: e.clientY, startScale: scale });
                }}
                onPointerMove={(e) => {
                  if (!resizingStamp || resizingStamp.id !== stamp.id) return;
                  const dy = resizingStamp.startY - e.clientY;
                  const newScale = resizingStamp.startScale + dy / 80;
                  const clamped = Math.max(0.4, Math.min(4, newScale));
                  const delta = clamped - (stamp.scale || 1);
                  if (onResizeStamp) onResizeStamp(stamp.id, delta);
                }}
                onPointerUp={() => setResizingStamp(null)}
                onPointerCancel={() => setResizingStamp(null)}
              />
            )}
          </div>
        );
      })}

      <div
        ref={overlayRef}
        className={`page-overlay-layer ${toolActive ? 'tool-active' : ''}`}
        style={{ cursor: cursorStyle }}
        onClick={handleClick}
        onPointerDown={handlePointerDown}
        onPointerMove={handlePointerMove}
        onPointerUp={handlePointerUp}
        onPointerCancel={handlePointerUp}
      />
    </div>
  );
}

window.ScoreView = ScoreView;
