// artwork-manager.jsx
// ─────────────────────────────────────────────────────────────────────────
// Multi-asset artwork manager for Add Release / Edition.
//
// Supports:
//   • Drag-drop or click upload (image OR video)
//   • Per-asset type dropdown sourced from REF.artwork_types
//   • Per-asset "Primary" toggle (only one front cover can be primary)
//   • Live spec validation: dimensions for images, dimensions+duration
//     for videos. Codec / bitrate / colour-space cannot be detected
//     in-browser without ffprobe — we surface the spec text so the user
//     can verify before delivery.
//   • Spec sidebar that updates with the *selected* asset's type.
//   • This is a CATALOG manager, not a distributor: nothing is mandatory.
//     Validation surfaces as advisory hints, never blocks.
//
// Mirrors astro.release_artwork shape:
//   { artwork_type, url, width_px, height_px, file_format, file_size_bytes,
//     is_primary }
//
// Props:
//   value     Array<asset>      current artwork list (lives on form.artwork)
//   onChange  (next) => void    receives full updated list
//
// Exposes window.ArtworkManager.
// ─────────────────────────────────────────────────────────────────────────

(function() {
  const { useState, useRef, useMemo, useEffect } = React;

  // ── SPEC TABLE ─────────────────────────────────────────────────────────
  // Keyed by ref.artwork_types.code. Specs are advisory (catalog manager
  // tolerates anything); these strings render in the right rail.
  const SPECS = {
    FrontCoverImage: {
      kind: 'image',
      title: 'Front Cover',
      formats: ['jpg', 'jpeg', 'tif', 'tiff', 'png'],
      minDim: 3000, maxDim: 6000,
      ratio: '1:1',
      maxBytes: 50 * 1024 * 1024,
      lines: [
        'Format: .jpg, .tif, .png',
        'Dimensions: 3000×3000 – 6000×6000 px',
        'Aspect ratio: 1:1',
        'Resolution: 300 dpi',
        'Color space: RGB',
        'Max file size: 50 MB',
      ],
    },
    BackCoverImage: {
      kind: 'image',
      title: 'Back Cover',
      formats: ['jpg', 'jpeg', 'tif', 'tiff', 'png'],
      minDim: 3000, ratio: '1:1', maxBytes: 50 * 1024 * 1024,
      lines: ['Format: .jpg, .tif, .png', '≥ 3000×3000 px, 1:1', 'RGB, ≤ 50 MB'],
    },
    BookletFrontImage: { kind: 'image', title: 'Booklet — Front', formats: ['jpg','jpeg','tif','tiff','png','pdf'], lines: ['Format: .jpg, .tif, .png, .pdf', 'Match physical booklet at 300 dpi'] },
    BookletBackImage:  { kind: 'image', title: 'Booklet — Back',  formats: ['jpg','jpeg','tif','tiff','png','pdf'], lines: ['Format: .jpg, .tif, .png, .pdf', 'Match physical booklet at 300 dpi'] },
    BookletImage:      { kind: 'image', title: 'Booklet — Page',  formats: ['jpg','jpeg','tif','tiff','png','pdf'], lines: ['Format: .jpg, .tif, .png, .pdf', 'Match physical booklet at 300 dpi'] },
    SpineImage:        { kind: 'image', title: 'Spine',           formats: ['jpg','jpeg','png','tif','tiff'], lines: ['Format: .jpg, .png, .tif', 'Spine of physical product'] },
    DiscImage:         { kind: 'image', title: 'Disc / Label',    formats: ['jpg','jpeg','png','tif','tiff'], minDim: 1417, ratio: '1:1', lines: ['Format: .jpg, .png, .tif', '≥ 1417×1417 px, circular crop', 'CD/Vinyl on-disc artwork'] },
    ArtistImage:       { kind: 'image', title: 'Artist Image',    formats: ['jpg','jpeg','png'], minDim: 2400, lines: ['Format: .jpg, .png', '≥ 2400×2400 px', 'For artist page on DSPs'] },
    TrayImage:         { kind: 'image', title: 'Tray',            formats: ['jpg','jpeg','png','tif','tiff'], lines: ['Physical product tray artwork'] },
    LogoImage:         { kind: 'image', title: 'Logo',            formats: ['png','svg'], lines: ['Format: .png with transparency, .svg', 'Vector preferred'] },
    BannerImage:       { kind: 'image', title: 'Banner',          formats: ['jpg','jpeg','png'], lines: ['Format: .jpg, .png', 'Wide aspect (≥ 16:9)'] },

    // ── Motion ──
    AlbumPageMotion3x4: {
      kind: 'video',
      title: 'Apple Album Page Motion (3:4)',
      formats: ['mp4', 'mov'],
      width: 2048, height: 2732,
      ratio: '3:4',
      duration: { min: 15, max: 35 },
      lines: [
        'Format: .mp4 (or .mov)',
        'Dimensions: 2048×2732 px (3:4)',
        'Codec: Apple ProRes 422 / 4444 or H.264',
        'Frame rate: 23.976, 24, 25, 29.97, or 30 fps',
        'Color space: Rec 709 or sRGB',
        'Length: 15–35 sec, no audio',
        'Bitrate: 45–100 Mbps',
      ],
    },
    AlbumPageMotion1x1: {
      kind: 'video',
      title: 'Apple Album Page Motion (1:1)',
      formats: ['mp4', 'mov'],
      width: 3840, height: 3840,
      ratio: '1:1',
      duration: { min: 15, max: 35 },
      lines: [
        'Format: .mp4 (or .mov)',
        'Dimensions: 3840×3840 px (1:1)',
        'Codec: Apple ProRes 422 / 4444 or H.264',
        'Frame rate: 23.976, 24, 25, 29.97, or 30 fps',
        'Color space: Rec 709 or sRGB',
        'Length: 15–35 sec, no audio',
        'Bitrate: 45–100 Mbps',
      ],
    },
    SpotifyCanvas: {
      kind: 'video',
      title: 'Spotify Canvas',
      formats: ['mp4'],
      width: 1080, height: 1920,
      ratio: '9:16',
      duration: { min: 3, max: 8 },
      lines: [
        'Format: .mp4',
        'Dimensions: 1080×1920 px (9:16)',
        'Length: 3–8 sec looping',
        'Per-track asset',
      ],
    },
    ArtistMotion: {
      kind: 'video',
      title: 'Artist Motion',
      formats: ['mp4', 'mov'],
      ratio: '1:1',
      lines: ['Format: .mp4 / .mov', 'Square motion for artist page', 'Looping, no audio preferred'],
    },
    AnimatedCover: {
      kind: 'video',
      title: 'Animated Cover',
      formats: ['mp4', 'mov', 'gif'],
      ratio: '1:1',
      lines: ['Format: .mp4 / .mov / .gif', 'Square looping animation of front cover'],
    },
    VideoTrailer: {
      kind: 'video',
      title: 'Video Trailer',
      formats: ['mp4', 'mov'],
      lines: ['Format: .mp4 / .mov', 'Short promo trailer for the release'],
    },
    LyricVideo: {
      kind: 'video',
      title: 'Lyric Video',
      formats: ['mp4', 'mov'],
      ratio: '16:9',
      lines: ['Format: .mp4 / .mov', 'Full lyric video asset'],
    },
  };

  // Default code suggestion based on file kind
  const defaultCodeFor = (kind) => kind === 'video' ? 'AlbumPageMotion1x1' : 'FrontCoverImage';

  // ── PROBE HELPERS ──────────────────────────────────────────────────────
  function probeImage(file) {
    return new Promise((resolve) => {
      const url = URL.createObjectURL(file);
      const img = new Image();
      img.onload = () => {
        resolve({ kind: 'image', url, width: img.naturalWidth, height: img.naturalHeight,
                  size: file.size, format: (file.name.split('.').pop() || '').toLowerCase(),
                  name: file.name });
      };
      img.onerror = () => resolve({ kind: 'image', url, width: 0, height: 0, size: file.size,
                                     format: (file.name.split('.').pop() || '').toLowerCase(),
                                     name: file.name });
      img.src = url;
    });
  }
  function probeVideo(file) {
    return new Promise((resolve) => {
      const url = URL.createObjectURL(file);
      const v = document.createElement('video');
      v.preload = 'metadata';
      v.muted = true;
      v.onloadedmetadata = () => {
        resolve({ kind: 'video', url, width: v.videoWidth, height: v.videoHeight,
                  duration: v.duration, size: file.size,
                  format: (file.name.split('.').pop() || '').toLowerCase(),
                  name: file.name });
      };
      v.onerror = () => resolve({ kind: 'video', url, width: 0, height: 0, duration: 0,
                                   size: file.size, format: (file.name.split('.').pop() || '').toLowerCase(),
                                   name: file.name });
      v.src = url;
    });
  }
  function probeFile(file) {
    const isVid = (file.type || '').startsWith('video/') ||
                  /\.(mp4|mov|m4v|webm|gif)$/i.test(file.name);
    return isVid ? probeVideo(file) : probeImage(file);
  }

  // ── VALIDATION ─────────────────────────────────────────────────────────
  // Returns { warnings: string[], ok: boolean }
  function validate(asset) {
    const spec = SPECS[asset.type_code];
    if (!spec) return { warnings: [], ok: true };
    const w = [];
    const m = asset.meta || {};
    if (spec.formats && m.format && !spec.formats.includes(m.format)) {
      w.push(`Format .${m.format} — spec wants ${spec.formats.map(f => '.'+f).join(' / ')}`);
    }
    if (spec.kind === 'image') {
      if (spec.minDim && m.width && m.height && (m.width < spec.minDim || m.height < spec.minDim)) {
        w.push(`Dimensions ${m.width}×${m.height} below ${spec.minDim}×${spec.minDim} min`);
      }
      if (spec.maxDim && m.width && m.height && (m.width > spec.maxDim || m.height > spec.maxDim)) {
        w.push(`Dimensions ${m.width}×${m.height} above ${spec.maxDim}×${spec.maxDim} max`);
      }
      if (spec.ratio === '1:1' && m.width && m.height && m.width !== m.height) {
        w.push(`Aspect ratio ${m.width}:${m.height} — spec wants 1:1`);
      }
    }
    if (spec.kind === 'video') {
      if (spec.width && spec.height && m.width && m.height &&
          (m.width !== spec.width || m.height !== spec.height)) {
        w.push(`Dimensions ${m.width}×${m.height} — spec wants ${spec.width}×${spec.height}`);
      }
      if (spec.duration && m.duration) {
        if (m.duration < spec.duration.min) w.push(`Duration ${m.duration.toFixed(1)}s below ${spec.duration.min}s min`);
        if (m.duration > spec.duration.max) w.push(`Duration ${m.duration.toFixed(1)}s above ${spec.duration.max}s max`);
      }
    }
    if (spec.maxBytes && m.size && m.size > spec.maxBytes) {
      w.push(`File size ${(m.size/1024/1024).toFixed(1)} MB above ${(spec.maxBytes/1024/1024).toFixed(0)} MB max`);
    }
    return { warnings: w, ok: w.length === 0 };
  }

  function fmtBytes(n) {
    if (!n) return '—';
    if (n < 1024) return n + ' B';
    if (n < 1024*1024) return (n/1024).toFixed(0) + ' KB';
    return (n/1024/1024).toFixed(1) + ' MB';
  }

  // ── ASSET TILE ─────────────────────────────────────────────────────────
  function AssetTile({ asset, types, isSelected, onSelect, onChange, onRemove, onMakePrimary }) {
    const { warnings } = validate(asset);
    const m = asset.meta || {};
    const hasWarn = warnings.length > 0;
    return (
      <div
        onClick={onSelect}
        style={{
          border: '1px solid ' + (isSelected ? 'var(--ink)' : 'var(--rule)'),
          background: 'var(--bg)',
          padding: 0,
          cursor: 'pointer',
          display: 'grid',
          gridTemplateColumns: '88px 1fr auto',
          gap: 0,
        }}>
        {/* Thumbnail */}
        <div style={{
          width: 88, height: 88, background: '#000',
          backgroundImage: m.kind === 'image' ? `url(${asset.url})` : 'none',
          backgroundSize: 'cover', backgroundPosition: 'center',
          position: 'relative', overflow: 'hidden',
        }}>
          {m.kind === 'video' &&
            <video src={asset.url} muted playsInline preload="metadata"
              style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
          }
          {m.kind === 'video' &&
            <div style={{ position: 'absolute', top: 4, left: 4,
                          background: 'rgba(0,0,0,.65)', color: '#fff',
                          fontSize: 8, padding: '2px 4px', letterSpacing: '.06em' }}
                 className="ff-mono">VIDEO</div>
          }
          {asset.is_primary &&
            <div style={{ position: 'absolute', bottom: 4, left: 4,
                          background: 'var(--ink)', color: 'var(--bg)',
                          fontSize: 8, padding: '2px 5px', letterSpacing: '.08em', fontWeight: 700 }}
                 className="ff-mono upper">PRIMARY</div>
          }
        </div>

        {/* Info */}
        <div style={{ padding: '8px 12px', minWidth: 0 }}>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 5 }}>
            <select value={asset.type_code}
              onClick={(e) => e.stopPropagation()}
              onChange={(e) => onChange({ ...asset, type_code: e.target.value })}
              className="ff-mono"
              style={{ fontSize: 10, padding: '4px 6px', background: 'var(--bg-2)',
                       border: '1px solid var(--rule)', color: 'var(--ink)', flex: '0 1 auto' }}>
              {types.map(t =>
                <option key={t.code} value={t.code}>{t.label}</option>
              )}
            </select>
            {hasWarn &&
              <span className="ff-mono upper" style={{ fontSize: 8, color: 'var(--warning, #d97757)',
                            letterSpacing: '.1em', fontWeight: 700 }}>
                ⚠ {warnings.length}
              </span>
            }
          </div>
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-2)',
                       overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {m.name || '—'}
          </div>
          <div className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 3, letterSpacing: '.04em' }}>
            {m.width && m.height ? `${m.width}×${m.height}` : '—'}
            {m.duration ? ` · ${m.duration.toFixed(1)}s` : ''}
            {' · '}{fmtBytes(m.size)}
            {m.format ? ` · .${m.format}` : ''}
          </div>
        </div>

        {/* Actions */}
        <div style={{ display: 'flex', flexDirection: 'column', borderLeft: '1px solid var(--rule)' }}>
          {!asset.is_primary &&
            <button onClick={(e) => { e.stopPropagation(); onMakePrimary(); }}
              className="ff-mono upper" title="Make primary"
              style={{ flex: 1, padding: '0 12px', background: 'transparent',
                       border: 'none', borderBottom: '1px solid var(--rule)',
                       color: 'var(--ink-3)', fontSize: 9, letterSpacing: '.1em', cursor: 'pointer' }}>
              ★
            </button>
          }
          <button onClick={(e) => { e.stopPropagation(); onRemove(); }}
            className="ff-mono upper" title="Remove"
            style={{ flex: 1, padding: '0 12px', background: 'transparent',
                     border: 'none', color: 'var(--ink-3)', fontSize: 11, cursor: 'pointer' }}>
            ×
          </button>
        </div>
      </div>
    );
  }

  // ── SPEC PANEL ─────────────────────────────────────────────────────────
  function SpecPanel({ asset }) {
    if (!asset) {
      return (
        <div style={{ border: '1px solid var(--rule)', background: 'var(--bg-2)',
                      padding: 16, fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.6 }}>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em',
                       color: 'var(--ink-3)', marginBottom: 8 }}>SPEC SHEET</div>
          Select an asset to see its delivery specification.
        </div>
      );
    }
    const spec = SPECS[asset.type_code];
    const { warnings } = validate(asset);
    return (
      <div style={{ border: '1px solid var(--rule)', background: 'var(--bg-2)', padding: 16 }}>
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em',
                     color: 'var(--ink-3)', marginBottom: 4 }}>SPEC SHEET</div>
        <div className="ff-display" style={{ fontSize: 16, fontWeight: 700,
                     marginBottom: 10, letterSpacing: '-0.01em' }}>
          {spec ? spec.title : asset.type_code}
        </div>
        {spec && spec.lines &&
          <ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
            {spec.lines.map((l, i) =>
              <li key={i} className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-2)',
                              padding: '3px 0', borderBottom: i < spec.lines.length - 1 ? '1px dashed var(--rule-soft)' : 'none' }}>
                {l}
              </li>
            )}
          </ul>
        }
        {warnings.length > 0 &&
          <div style={{ marginTop: 14, padding: 10,
                        border: '1px solid var(--warning, #d97757)',
                        background: 'rgba(217,119,87,.06)' }}>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--warning, #d97757)',
                         letterSpacing: '.12em', marginBottom: 6, fontWeight: 700 }}>
              ⚠ Spec Mismatch · Advisory
            </div>
            {warnings.map((w, i) =>
              <div key={i} className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-2)',
                              lineHeight: 1.55, padding: '2px 0' }}>
                · {w}
              </div>
            )}
            <div className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)',
                         marginTop: 8, fontStyle: 'italic' }}>
              Catalog tolerates non-spec assets. Fix before delivery.
            </div>
          </div>
        }
      </div>
    );
  }

  // ── MAIN ───────────────────────────────────────────────────────────────
  function ArtworkManager({ value, onChange }) {
    const list = value || [];
    const [selectedId, setSelectedId] = useState(list[0] ? list[0].id : null);
    const [dragOver, setDragOver] = useState(false);
    const inputRef = useRef(null);

    const types = useMemo(() => {
      const ref = (window.REF && window.REF.artwork_types) || [];
      return ref.length ? ref : Object.keys(SPECS).map((code, i) => ({ id: i+1, code, label: SPECS[code].title }));
    }, []);

    const selected = list.find(a => a.id === selectedId) || null;

    async function handleFiles(files) {
      const arr = Array.from(files || []);
      if (!arr.length) return;
      const probed = await Promise.all(arr.map(probeFile));
      const newAssets = probed.map((meta, i) => {
        const code = defaultCodeFor(meta.kind);
        return {
          id: 'aw_' + Date.now() + '_' + i + '_' + Math.random().toString(36).slice(2, 6),
          file: arr[i],
          url: meta.url,
          type_code: code,
          is_primary: false,
          kind: meta.kind,
          meta,
        };
      });
      // First-ever upload of a FrontCoverImage becomes primary
      const merged = [...list, ...newAssets];
      const hasPrimary = merged.some(a => a.is_primary);
      if (!hasPrimary) {
        const firstFront = merged.find(a => a.type_code === 'FrontCoverImage');
        const firstAny = firstFront || merged[0];
        if (firstAny) firstAny.is_primary = true;
      }
      onChange(merged);
      if (!selectedId && newAssets[0]) setSelectedId(newAssets[0].id);
    }

    function updateAsset(next) {
      onChange(list.map(a => a.id === next.id ? next : a));
    }
    function removeAsset(id) {
      const next = list.filter(a => a.id !== id);
      // If we removed the primary, promote first remaining
      if (next.length && !next.some(a => a.is_primary)) {
        next[0] = { ...next[0], is_primary: true };
      }
      onChange(next);
      if (selectedId === id) setSelectedId(next[0] ? next[0].id : null);
    }
    function makePrimary(id) {
      onChange(list.map(a => ({ ...a, is_primary: a.id === id })));
    }

    function onDrop(e) {
      e.preventDefault();
      setDragOver(false);
      handleFiles(e.dataTransfer.files);
    }

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 18 }}>
        {/* LEFT — drop zone + list */}
        <div>
          <div
            onClick={() => inputRef.current && inputRef.current.click()}
            onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
            onDragLeave={() => setDragOver(false)}
            onDrop={onDrop}
            style={{
              border: '2px dashed ' + (dragOver ? 'var(--ink)' : 'var(--rule)'),
              background: dragOver ? 'var(--bg-2)' : 'transparent',
              padding: '28px 20px',
              textAlign: 'center',
              cursor: 'pointer',
              marginBottom: 14,
              transition: 'border-color 80ms, background 80ms',
            }}>
            <div className="ff-display" style={{ fontSize: 18, fontWeight: 700, marginBottom: 4, letterSpacing: '-0.01em' }}>
              Drop artwork here
            </div>
            <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)', letterSpacing: '.04em' }}>
              or click to browse · images and motion video · multiple files OK
            </div>
            <input ref={inputRef} type="file" multiple
              accept="image/*,video/*,application/pdf"
              onChange={(e) => { handleFiles(e.target.files); e.target.value = ''; }}
              style={{ display: 'none' }} />
          </div>

          {list.length === 0 &&
            <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)',
                         padding: '20px 0', textAlign: 'center', lineHeight: 1.6 }}>
              No artwork uploaded yet.<br />
              <span style={{ fontSize: 10 }}>You can add it now or later from the release page — nothing is required to save.</span>
            </div>
          }

          {list.length > 0 &&
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {list.map(a =>
                <AssetTile key={a.id} asset={a} types={types}
                  isSelected={a.id === selectedId}
                  onSelect={() => setSelectedId(a.id)}
                  onChange={updateAsset}
                  onRemove={() => removeAsset(a.id)}
                  onMakePrimary={() => makePrimary(a.id)} />
              )}
            </div>
          }

          {list.length > 0 &&
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)',
                         marginTop: 12, lineHeight: 1.55 }}>
              {list.length} asset{list.length === 1 ? '' : 's'} ·{' '}
              {list.filter(a => a.kind === 'image').length} image,{' '}
              {list.filter(a => a.kind === 'video').length} motion ·{' '}
              {list.some(a => a.is_primary) ? 'primary set' : 'no primary'}
            </div>
          }
        </div>

        {/* RIGHT — spec sidebar */}
        <div>
          <SpecPanel asset={selected} />
        </div>
      </div>
    );
  }

  window.ArtworkManager = ArtworkManager;
})();
