/* Vista previa de foto con arrastre y zoom para recortar (exporta JPEG) */

const PhotoCropUploader = ({ theme, initialUrl, onBlobReady, size = 200 }) => {
  const [src, setSrc] = React.useState(initialUrl || null);
  const [objectUrl, setObjectUrl] = React.useState(null);
  const [offset, setOffset] = React.useState({ x: 0, y: 0 });
  const [zoom, setZoom] = React.useState(1);
  const [imgSize, setImgSize] = React.useState({ w: 0, h: 0 });
  const [dragging, setDragging] = React.useState(false);
  const dragStart = React.useRef(null);
  const fileRef = React.useRef(null);
  const imgRef = React.useRef(null);
  const frameRef = React.useRef(null);
  const offsetRef = React.useRef(offset);
  offsetRef.current = offset;

  const coverScale = imgSize.w && imgSize.h
    ? Math.max(size / imgSize.w, size / imgSize.h)
    : 1;

  const displayW = imgSize.w * coverScale * zoom;
  const displayH = imgSize.h * coverScale * zoom;
  const maxOffX = Math.max(0, (displayW - size) / 2);
  const maxOffY = Math.max(0, (displayH - size) / 2);

  const clampOffset = (x, y, mx = maxOffX, my = maxOffY) => ({
    x: Math.min(mx, Math.max(-mx, x)),
    y: Math.min(my, Math.max(-my, y)),
  });

  React.useEffect(() => {
    setSrc(initialUrl || null);
    setOffset({ x: 0, y: 0 });
    setZoom(1);
    setImgSize({ w: 0, h: 0 });
  }, [initialUrl]);

  React.useEffect(() => () => {
    if (objectUrl) URL.revokeObjectURL(objectUrl);
  }, [objectUrl]);

  React.useEffect(() => {
    setOffset(o => clampOffset(o.x, o.y));
  }, [zoom, maxOffX, maxOffY]);

  const onImgLoad = () => {
    const img = imgRef.current;
    if (!img?.naturalWidth) return;
    setImgSize({ w: img.naturalWidth, h: img.naturalHeight });
    setOffset({ x: 0, y: 0 });
    setZoom(1);
  };

  const loadFile = (file) => {
    if (!file || !file.type.startsWith('image/')) return;
    if (objectUrl) URL.revokeObjectURL(objectUrl);
    const url = URL.createObjectURL(file);
    setObjectUrl(url);
    setSrc(url);
    setOffset({ x: 0, y: 0 });
    setZoom(1);
    setImgSize({ w: 0, h: 0 });
  };

  const onPointerDown = (e) => {
    if (!src || maxOffX < 0.5 && maxOffY < 0.5) return;
    e.preventDefault();
    e.stopPropagation();
    setDragging(true);
    dragStart.current = {
      x: e.clientX,
      y: e.clientY,
      offset: { ...offsetRef.current },
    };
    e.currentTarget.setPointerCapture(e.pointerId);
  };

  const onPointerMove = (e) => {
    if (!dragging || !dragStart.current) return;
    e.preventDefault();
    const dx = e.clientX - dragStart.current.x;
    const dy = e.clientY - dragStart.current.y;
    setOffset(clampOffset(
      dragStart.current.offset.x + dx,
      dragStart.current.offset.y + dy,
    ));
  };

  const onPointerUp = (e) => {
    if (!dragging) return;
    setDragging(false);
    dragStart.current = null;
    try {
      e.currentTarget.releasePointerCapture(e.pointerId);
    } catch { /* ya liberado */ }
  };

  const exportBlob = React.useCallback(async () => {
    if (!src || !imgRef.current || !imgSize.w) return null;
    const img = imgRef.current;
    await new Promise((res, rej) => {
      if (img.complete) res();
      else { img.onload = res; img.onerror = rej; }
    });
    const out = 512;
    const canvas = document.createElement('canvas');
    canvas.width = out;
    canvas.height = out;
    const ctx = canvas.getContext('2d');
    const cs = Math.max(size / img.naturalWidth, size / img.naturalHeight);
    const totalScale = cs * zoom;
    const dw = img.naturalWidth * totalScale;
    const dh = img.naturalHeight * totalScale;
    const imgLeft = size / 2 - dw / 2 + offset.x;
    const imgTop = size / 2 - dh / 2 + offset.y;
    const sx = Math.max(0, (-imgLeft) / totalScale);
    const sy = Math.max(0, (-imgTop) / totalScale);
    const sw = size / totalScale;
    const sh = size / totalScale;
    ctx.fillStyle = '#111';
    ctx.fillRect(0, 0, out, out);
    ctx.drawImage(img, sx, sy, sw, sh, 0, 0, out, out);
    return new Promise(resolve => canvas.toBlob(b => resolve(b), 'image/jpeg', 0.92));
  }, [src, offset, zoom, imgSize.w, size]);

  React.useEffect(() => {
    if (!onBlobReady) return;
    const t = setTimeout(async () => {
      if (!src) { onBlobReady(null); return; }
      const blob = await exportBlob();
      onBlobReady(blob);
    }, 120);
    return () => clearTimeout(t);
  }, [src, offset, zoom, imgSize, exportBlob, onBlobReady]);

  const canPan = maxOffX > 0.5 || maxOffY > 0.5;
  const preview = src;

  return (
    <div style={{ display: 'grid', gap: 10 }}>
      <input ref={fileRef} type="file" accept="image/jpeg,image/png,image/webp" hidden
        onChange={e => loadFile(e.target.files?.[0])} />
      <div
        ref={frameRef}
        onDragOver={e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }}
        onDrop={e => { e.preventDefault(); loadFile(e.dataTransfer.files?.[0]); }}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        onPointerCancel={onPointerUp}
        style={{
          width: size, height: size, margin: '0 auto', borderRadius: 16, overflow: 'hidden',
          border: `2px dashed ${dragging ? theme.primary : `${theme.primary}66`}`,
          background: theme.bgInput,
          cursor: preview ? (canPan ? (dragging ? 'grabbing' : 'grab') : 'default') : 'pointer',
          position: 'relative', touchAction: 'none',
          userSelect: 'none',
        }}
        onClick={() => !preview && fileRef.current?.click()}
      >
        {preview && (
          <img
            ref={imgRef}
            src={preview}
            alt=""
            draggable={false}
            onLoad={onImgLoad}
            style={imgSize.w > 0 ? {
              position: 'absolute',
              left: '50%',
              top: '50%',
              width: displayW,
              height: displayH,
              marginLeft: -displayW / 2 + offset.x,
              marginTop: -displayH / 2 + offset.y,
              userSelect: 'none',
              pointerEvents: 'none',
            } : {
              position: 'absolute',
              width: 0,
              height: 0,
              opacity: 0,
              pointerEvents: 'none',
            }}
          />
        )}
        {!preview && (
          <div style={{
            position: 'absolute', inset: 0, display: 'grid', placeItems: 'center',
            textAlign: 'center', padding: 16, color: theme.textDim, fontSize: 12, fontFamily: 'Inter, sans-serif',
          }}>
            <Icon name="upload" size={28} style={{ color: theme.primary, marginBottom: 8 }} />
            Arrastra la foto o haz clic
          </div>
        )}
      </div>
      {preview && (
        <>
          <label style={{ fontSize: 11, color: theme.textMute, fontFamily: 'Inter, sans-serif' }}>
            Zoom · {Math.round(zoom * 100)}%
            <input
              type="range"
              min="1"
              max="2.5"
              step="0.05"
              value={zoom}
              onChange={e => setZoom(Number(e.target.value))}
              onPointerDown={e => e.stopPropagation()}
              style={{ width: '100%', marginTop: 4, touchAction: 'auto' }}
            />
          </label>
          <p style={{ fontSize: 11, color: theme.textDim, margin: 0, textAlign: 'center', fontFamily: 'Inter, sans-serif', lineHeight: 1.45 }}>
            {canPan
              ? 'Arrastra la imagen para centrar · sube el zoom y vuelve a arrastrar'
              : 'Sube el zoom para poder mover la imagen'}
          </p>
          <Btn theme={theme} kind="ghost" size="sm" icon="upload" onClick={() => fileRef.current?.click()} style={{ justifySelf: 'center' }}>
            Cambiar foto
          </Btn>
        </>
      )}
    </div>
  );
};

Object.assign(window, { PhotoCropUploader });
