// Card view: inline-editable shot cards + drag-and-drop reorder
const { useState: useStateCards, useRef: useRefCards } = React;

const STATUSES = {
  approved: { label: 'Approved', cls: 'approved' },
  review:   { label: 'Needs Review', cls: 'review' },
  draft:    { label: 'Draft', cls: 'draft' },
};

const SHOT_SIZES = [
  'Extreme Close-Up',
  'Close-Up',
  'Medium Close-Up',
  'Medium Shot',
  'Medium Wide Shot',
  'Wide Shot',
  'Extreme Wide Shot',
  'Over the Shoulder',
  'Point of View',
  'Insert',
  'Graphic',
  'Custom…',
];

function StatusPill({ status, onClick, compact }) {
  const s = STATUSES[status];
  return (
    <button
      className={`status-pill ${s.cls}`}
      onClick={onClick}
      title="Click to change"
    >
      <span className="pulse" />
      {s.label}
    </button>
  );
}

function StatusMenu({ value, onPick, onClose, anchor }) {
  React.useEffect(() => {
    const h = (e) => { if (!e.target.closest('.menu')) onClose(); };
    setTimeout(() => document.addEventListener('click', h), 0);
    return () => document.removeEventListener('click', h);
  }, []);
  const style = anchor ? {
    position: 'fixed',
    top: anchor.bottom + 6,
    left: Math.max(8, anchor.right - 170),
  } : {};
  return (
    <div className="menu" style={style}>
      {Object.entries(STATUSES).map(([k, s]) => (
        <button key={k} className="menu-item" onClick={() => { onPick(k); onClose(); }}>
          <span className="swatch" style={{
            background: k==='approved' ? 'var(--status-approved)' : k==='review' ? 'var(--status-review)' : 'var(--status-draft)',
            color: k==='approved' ? 'var(--status-approved)' : k==='review' ? 'var(--status-review)' : 'var(--status-draft)'
          }} />
          {s.label}
          {value === k && <IconCheck style={{ width: 12, height: 12, marginLeft: 'auto', color: 'var(--accent)' }} />}
        </button>
      ))}
    </div>
  );
}

function readFileAsDataURL(file) {
  return new Promise((res, rej) => {
    const r = new FileReader();
    r.onload = () => res(r.result);
    r.onerror = rej;
    r.readAsDataURL(file);
  });
}

const FIELD_ICONS = {
  shot: 'IconShotType', camera: 'IconCameraBody',
  lens: 'IconAperture', move: 'IconArrows', duration: 'IconClock', location: 'IconLocation'
};

function ShotCard({ shot, displayNum, ar, density, dataConfig, onUpdate, onChangeStatus, onDelete, onDuplicate, dragHandlers, dragState, projectId, onViewComments, commentCount = 0, unresolved = 0 }) {
  const [menuAnchor, setMenuAnchor] = useStateCards(null);
  const [fileOver, setFileOver] = useStateCards(false);
  const fileInput = useRefCards(null);
  const cardRef = useRefCards(null);

  const upd = (k, v) => onUpdate(shot.id, { [k]: v });

  const handleFile = async (file) => {
    if (!file || !file.type.startsWith('image/')) return;
    // Capture the existing image so we can clean up its cloud file after the
    // new upload settles. Inline data URLs need no cleanup.
    const previousImg = shot.img;
    // 0) Downscale + re-encode locally first — caps resolution so cloud storage,
    //    bandwidth, and the localStorage data-URL fallback all stay small.
    const optimized = (typeof window.ppDownscaleImage === 'function')
      ? await window.ppDownscaleImage(file)
      : file;
    // 1) Show it immediately via a data URL — instant feedback.
    const dataUrl = await readFileAsDataURL(optimized);
    upd('img', dataUrl);
    // 2) If we're signed in, upload to Supabase Storage and swap to the
    //    returned public URL. Optimistic; if the upload fails we keep the
    //    data URL fallback (it still works, just stays inline).
    if (typeof window.ppUploadImage === 'function' && projectId) {
      const uid = await window.ppGetUserIdAsync?.();
      if (uid) {
        const url = await window.ppUploadImage(projectId, shot.id, optimized);
        if (url) {
          upd('img', url);
          // Delete the prior cloud file (if any) now that the new one is live.
          if (previousImg && typeof window.ppDeleteImage === 'function') {
            window.ppDeleteImage(previousImg);
          }
        }
      }
    }
  };

  // True when the current drag carries OS files (vs. a card being reordered).
  const dragHasFiles = (e) => Array.from(e.dataTransfer.types || []).includes('Files');

  // Image-drop handler — only acts on file drops; a card-reorder drop is left
  // to bubble up to the card's own onDrop.
  const onDrop = (e) => {
    const f = e.dataTransfer.files && e.dataTransfer.files[0];
    if (!f) return;
    e.preventDefault();
    e.stopPropagation();
    setFileOver(false);
    handleFile(f);
  };

  const cardCls = [
    'shot-card',
    `density-${density || 'compact'}`,
    dragState?.dragging ? 'dragging' : '',
    dragState?.dropBefore ? 'drop-before' : '',
    dragState?.dropAfter ? 'drop-after' : '',
  ].filter(Boolean).join(' ');

  // Reorder is wired only when the parent supplies drag handlers (scene view).
  const canReorder = !!(dragHandlers && dragHandlers.onDragStart);

  return (
    <div
      ref={cardRef}
      className={cardCls}
      onDragStart={dragHandlers?.onDragStart}
      onDragOver={dragHandlers?.onDragOver}
      onDragLeave={dragHandlers?.onDragLeave}
      onDrop={dragHandlers?.onDrop}
      onDragEnd={(e) => {
        // Re-disable dragging once the gesture ends so the card's inputs
        // stay normally interactive.
        if (cardRef.current) cardRef.current.draggable = false;
        dragHandlers?.onDragEnd?.(e);
      }}
    >
      <div
        className={`shot-thumb ar-${ar} ${fileOver ? 'file-over' : ''}`}
        onDragOver={(e) => { if (dragHasFiles(e)) { e.preventDefault(); setFileOver(true); } }}
        onDragLeave={() => setFileOver(false)}
        onDrop={onDrop}
      >
        {shot.img ? (
          <div className="thumb-img" style={{ backgroundImage: `url(${shot.img})` }} />
        ) : (
          <div className="thumb-empty" onClick={() => fileInput.current?.click()}>
            <IconUpload />
            <span className="upload-hint">Click or drop image</span>
          </div>
        )}
        <input
          ref={fileInput}
          type="file"
          accept="image/*"
          style={{ display: 'none' }}
          onChange={(e) => handleFile(e.target.files[0])}
        />
        <div className="shot-num-badge">{displayNum}</div>
        {canReorder && (
          <button
            className="card-drag-handle"
            title="Drag to reorder"
            /* A <button> swallows the drag gesture, so the card itself is
               only made draggable for the duration of a handle press. */
            onMouseDown={() => { if (cardRef.current) cardRef.current.draggable = true; }}
            onMouseUp={() => { if (cardRef.current) cardRef.current.draggable = false; }}
          >
            <IconGrip />
          </button>
        )}
        {shot.img && (
          <div className="shot-thumb-overlay">
            <button
              className="thumb-action"
              title="Replace image"
              onClick={(e) => { e.stopPropagation(); fileInput.current?.click(); }}
            >
              <IconImage />
            </button>
            <button
              className="thumb-action"
              title="Remove image"
              onClick={(e) => {
                e.stopPropagation();
                // Delete the storage file if it's a cloud URL; data: URLs
                // and missing files no-op.
                if (typeof window.ppDeleteImage === 'function') {
                  window.ppDeleteImage(shot.img);
                }
                upd('img', '');
              }}
            >
              <IconTrash />
            </button>
          </div>
        )}
      </div>

      <div className="shot-body">
        <input
          className="inline-input title"
          value={shot.title}
          placeholder="Untitled shot"
          onChange={(e) => upd('title', e.target.value)}
        />
        <textarea
          className="inline-textarea"
          rows={2}
          value={shot.desc}
          placeholder="Describe the action, blocking, key beats…"
          onChange={(e) => upd('desc', e.target.value)}
        />

        <div className={`shot-form-grid ${density === 'full' ? 'full' : ''}`}>
          {(() => {
            const order = dataConfig.order && dataConfig.order.length
              ? dataConfig.order
              : [
                  ...window.BUILTIN_FIELDS.map(f => ({ key: f.key, kind: 'builtin' })),
                  ...dataConfig.custom.map(c => ({ key: c.key, kind: 'custom' })),
                ];
            return order.map(o => {
              if (o.kind === 'builtin') {
                const f = window.BUILTIN_FIELDS.find(x => x.key === o.key);
                if (!f || !dataConfig.enabled[f.key]) return null;
                const Icon = window[FIELD_ICONS[f.key]];
                return (
                  <div key={f.key} className="shot-form-row">
                    <Icon className="row-icon" />
                    {density === 'full' && <span className="row-label-full">{f.label}</span>}
                    {f.key === 'shot' ? (
                      <ShotTypeField value={shot.shot} onChange={(v) => upd('shot', v)} />
                    ) : (
                      <input
                        className={`row-control ${!shot[f.key] ? 'is-empty' : ''}`}
                        value={shot[f.key] || ''}
                        onChange={(e) => upd(f.key, e.target.value)}
                        placeholder="—"
                      />
                    )}
                  </div>
                );
              }
              const cf = dataConfig.custom.find(c => c.key === o.key);
              if (!cf) return null;
              return (
                <div key={cf.key} className="shot-form-row">
                  <span className="row-icon di-dot-icon"><span className="di-dot di-dot-grey" /></span>
                  {density === 'full' && <span className="row-label-full">{cf.label}</span>}
                  <input
                    className={`row-control ${!shot.custom?.[cf.key] ? 'is-empty' : ''}`}
                    value={shot.custom?.[cf.key] || ''}
                    onChange={(e) => upd('custom', { ...(shot.custom || {}), [cf.key]: e.target.value })}
                    placeholder="—"
                  />
                </div>
              );
            });
          })()}
        </div>

        <div className="shot-status-row">
          <div className="status-left">
            <StatusPill
              status={shot.status}
              onClick={(e) => {
                e.stopPropagation();
                const r = e.currentTarget.getBoundingClientRect();
                setMenuAnchor(r);
              }}
            />
            <button
              className={`card-comments-btn ${unresolved > 0 ? 'has-open' : ''}`}
              title="View client comments"
              onClick={(e) => { e.stopPropagation(); if (onViewComments) onViewComments(shot.id); }}
            >
              <IconCommentCard />
              {commentCount > 0
                ? <span className="cc-count">{commentCount}</span>
                : <span className="cc-label">Comments</span>}
            </button>
          </div>
          <div className="quick-actions">
            <button className="icon-btn" title="Duplicate" onClick={() => onDuplicate(shot.id)}><IconDuplicate /></button>
            <button className="icon-btn" title="Delete" onClick={() => onDelete(shot.id)}><IconTrash /></button>
          </div>
        </div>
      </div>

      {menuAnchor && (
        <StatusMenu
          value={shot.status}
          anchor={menuAnchor}
          onPick={(k) => onChangeStatus(shot.id, k)}
          onClose={() => setMenuAnchor(null)}
        />
      )}
    </div>
  );
}

// arrows icon for camera move (defined here to avoid editing icons.jsx)
const IconArrows = (props) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...props}>
    <path d="M4 12h16M4 12l4-4M4 12l4 4M20 12l-4-4M20 12l-4 4"/>
  </svg>
);

// comment bubble — for the per-card "View comments" button (owner inbox)
const IconCommentCard = (props) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...props}>
    <path d="M21 11.5a8.38 8.38 0 0 1-8.5 8.5 9 9 0 0 1-4-1L3 20l1-5.5a8.38 8.38 0 0 1-1-4A8.5 8.5 0 0 1 11.5 2 8.5 8.5 0 0 1 21 11.5z"/>
  </svg>
);

function AddShotCard({ onClick }) {
  return (
    <button className="shot-card add-card" onClick={onClick}>
      <div className="add-card-icon"><IconPlus /></div>
      <div>Add a shot</div>
    </button>
  );
}

function SceneGroup({ scene, ar, density, dataConfig, collapsed, onToggle, onUpdate, onUpdateScene, onDeleteScene, onChangeStatus, onAddShot, onDelete, onDuplicate, onReorder, startNum, projectId, onViewComments, commentCounts }) {
  const bodyRef = useRefCards(null);
  const [maxH, setMaxH] = useStateCards('none');
  const [drag, setDrag] = useStateCards(null); // { fromShotId, overId, side }
  const [cols, setCols] = useStateCards(1);

  React.useEffect(() => {
    if (collapsed) {
      setMaxH('0px');
    } else if (bodyRef.current) {
      setMaxH(bodyRef.current.scrollHeight + 'px');
      const t = setTimeout(() => setMaxH('none'), 420);
      return () => clearTimeout(t);
    }
  }, [collapsed, scene.shots.length, ar]);

  // Detect grid columns to know whether the last row is full → thin add bar.
  React.useEffect(() => {
    if (!bodyRef.current) return;
    const measure = () => {
      const el = bodyRef.current;
      if (!el) return;
      const cs = getComputedStyle(el);
      const tracks = cs.gridTemplateColumns.split(' ').filter(Boolean).length;
      setCols(Math.max(1, tracks));
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(bodyRef.current);
    window.addEventListener('resize', measure);
    return () => { ro.disconnect(); window.removeEventListener('resize', measure); };
  }, []);

  const lastRowFull = scene.shots.length > 0 && (scene.shots.length % cols === 0);

  const dragHandlersFor = (shotId) => ({
    draggable: true,
    onDragStart: (e) => {
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', shotId);
      setDrag({ fromShotId: shotId, overId: null, side: null });
    },
    onDragOver: (e) => {
      e.preventDefault();
      if (!drag || drag.fromShotId === shotId) return;
      const r = e.currentTarget.getBoundingClientRect();
      const side = (e.clientY - r.top) < r.height / 2 ? 'before' : 'after';
      setDrag(d => d && (d.overId !== shotId || d.side !== side) ? { ...d, overId: shotId, side } : d);
    },
    onDragLeave: () => {},
    onDrop: (e) => {
      e.preventDefault();
      if (!drag || drag.fromShotId === shotId) { setDrag(null); return; }
      onReorder(scene.id, drag.fromShotId, shotId, drag.side);
      setDrag(null);
    },
    onDragEnd: () => setDrag(null),
  });

  return (
    <section className={`scene ${collapsed ? 'collapsed' : ''}`}>
      <div className="scene-header">
        <div className="scene-chev" onClick={onToggle}><IconChevDown /></div>
        <span className="scene-num" onClick={onToggle}>SC {scene.number}</span>
        <div className="scene-title-wrap">
          <input
            className="scene-title-input"
            value={scene.title}
            placeholder="Untitled scene"
            onChange={(e) => onUpdateScene(scene.id, { title: e.target.value })}
            onClick={(e) => e.stopPropagation()}
          />
          <span className="scene-count" onClick={onToggle}>{scene.shots.length} {scene.shots.length === 1 ? 'shot' : 'shots'}</span>
        </div>
        <div className="scene-line" onClick={onToggle} />
        <div className="scene-actions" onClick={(e) => e.stopPropagation()}>
          <button className="icon-btn" title="Add shot to scene" onClick={() => onAddShot(scene.id)}><IconPlus /></button>
          <button className="icon-btn" title="Delete scene" onClick={() => onDeleteScene(scene.id)}><IconTrash /></button>
          <button className="icon-btn" title="More"><IconMore /></button>
        </div>
      </div>
      <div className={`scene-body ${lastRowFull ? '' : 'has-card-add'}`}>
        <div className="cards-grid" ref={bodyRef} style={{ maxHeight: maxH }}>
          {scene.shots.map((s, i) => {
            const isDragging = drag?.fromShotId === s.id;
            const isOver = drag?.overId === s.id;
            return (
              <ShotCard
                key={s.id}
                shot={s}
                displayNum={startNum + i}
                ar={ar}
                density={density}
                dataConfig={dataConfig}
                onUpdate={onUpdate}
                onChangeStatus={onChangeStatus}
                onDelete={onDelete}
                onDuplicate={onDuplicate}
                projectId={projectId}
                onViewComments={onViewComments}
                commentCount={commentCounts && commentCounts[s.id] ? commentCounts[s.id].total : 0}
                unresolved={commentCounts && commentCounts[s.id] ? commentCounts[s.id].unresolved : 0}
                dragHandlers={dragHandlersFor(s.id)}
                dragState={{
                  dragging: isDragging,
                  dropBefore: isOver && drag.side === 'before',
                  dropAfter: isOver && drag.side === 'after',
                }}
              />
            );
          })}
          {!lastRowFull && <AddShotCard onClick={() => onAddShot(scene.id)} />}
        </div>
        {lastRowFull && (
          <button className="add-shot-bar" onClick={() => onAddShot(scene.id)}>
            <IconPlus /> Add a shot
          </button>
        )}
      </div>
    </section>
  );
}

// Shot type field: dropdown of named sizes + custom freeform option
function ShotTypeField({ value, onChange }) {
  const presets = SHOT_SIZES.filter(s => s !== 'Custom…');
  const isPreset = presets.includes(value);
  const [mode, setMode] = useStateCards(!value || isPreset ? 'preset' : 'custom');
  const sel = (e) => {
    if (e.target.value === 'Custom…') {
      setMode('custom');
      onChange('');
    } else {
      onChange(e.target.value);
    }
  };
  if (mode === 'custom') {
    return (
      <input
        className={`row-control ${!value ? 'is-empty' : ''}`}
        value={value || ''}
        autoFocus
        placeholder="Custom shot type"
        onChange={(e) => onChange(e.target.value)}
        onBlur={() => { if (!value) setMode('preset'); }}
      />
    );
  }
  return (
    <select className={`row-control ${!value ? 'is-empty' : ''}`} value={value || ''} onChange={sel}>
      <option value="">—</option>
      {SHOT_SIZES.map(s => <option key={s}>{s}</option>)}
    </select>
  );
}

// Flat shots grid — no scene grouping, single grid of all shots.
// Drag-reorder works here too: a shot can be moved anywhere in the flat list,
// including across scene boundaries, via the app's reorderAcrossScenes.
function FlatShotsGrid({ scenes, ar, density, dataConfig, onUpdate, onChangeStatus, onDelete, onDuplicate, onAddShot, onReorder, projectId, onViewComments, commentCounts }) {
  const [drag, setDrag] = useStateCards(null);   // { fromShotId, fromSceneId, overId, side }

  // Flatten, remembering each shot's owning scene.
  const flat = [];
  scenes.forEach(sc => sc.shots.forEach(s => flat.push({ shot: s, sceneId: sc.id })));
  const sceneIdOf = (shotId) => {
    const e = flat.find(x => x.shot.id === shotId);
    return e ? e.sceneId : null;
  };

  const dragHandlersFor = (shotId) => ({
    draggable: true,
    onDragStart: (e) => {
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', shotId);
      setDrag({ fromShotId: shotId, fromSceneId: sceneIdOf(shotId), overId: null, side: null });
    },
    onDragOver: (e) => {
      e.preventDefault();
      if (!drag || drag.fromShotId === shotId) return;
      const r = e.currentTarget.getBoundingClientRect();
      const side = (e.clientY - r.top) < r.height / 2 ? 'before' : 'after';
      setDrag(d => d && (d.overId !== shotId || d.side !== side) ? { ...d, overId: shotId, side } : d);
    },
    onDragLeave: () => {},
    onDrop: (e) => {
      e.preventDefault();
      if (!drag || drag.fromShotId === shotId) { setDrag(null); return; }
      if (onReorder) onReorder(drag.fromSceneId, drag.fromShotId, sceneIdOf(shotId), shotId, drag.side);
      setDrag(null);
    },
    onDragEnd: () => setDrag(null),
  });

  return (
    <section className="scene flat-shots">
      <div className="scene-body">
        <div className="cards-grid">
          {flat.map((entry, i) => {
            const s = entry.shot;
            const isDragging = drag?.fromShotId === s.id;
            const isOver = drag?.overId === s.id;
            return (
              <ShotCard
                key={s.id}
                shot={s}
                displayNum={i + 1}
                ar={ar}
                density={density}
                dataConfig={dataConfig}
                onUpdate={onUpdate}
                onChangeStatus={onChangeStatus}
                onDelete={onDelete}
                onDuplicate={onDuplicate}
                projectId={projectId}
                onViewComments={onViewComments}
                commentCount={commentCounts && commentCounts[s.id] ? commentCounts[s.id].total : 0}
                unresolved={commentCounts && commentCounts[s.id] ? commentCounts[s.id].unresolved : 0}
                dragHandlers={dragHandlersFor(s.id)}
                dragState={{
                  dragging: isDragging,
                  dropBefore: isOver && drag.side === 'before',
                  dropAfter: isOver && drag.side === 'after',
                }}
              />
            );
          })}
          <AddShotCard onClick={onAddShot} />
        </div>
      </div>
    </section>
  );
}

window.FlatShotsGrid = FlatShotsGrid;

Object.assign(window, { ShotCard, AddShotCard, SceneGroup, StatusPill, StatusMenu, STATUSES, IconArrows, ShotTypeField, SHOT_SIZES });
