// Dev-only curation tool: mark photos/videos for archival.
//
// When `location.hostname === 'localhost'` OR `?dev=1` is in the URL, a
// small `<DevArchiveWidget>` appears bottom-right and an `<DevArchiveButton>`
// overlays each stage media item. Clicking the × adds that item to a
// localStorage-backed queue; the widget lets you export the queue as a
// JSON file the Python apply-script consumes.
//
// Gated to dev mode so public visitors never see the UI (and couldn't
// mutate R2 anyway — queue only exists in their browser).

(() => {
  const KEY = 'archive-queue-v1';
  const COVER_KEY = 'cover-queue-v1';

  const isDevMode = () => {
    if (typeof window === 'undefined') return false;
    if (new URLSearchParams(window.location.search).get('dev') === '1') return true;
    const host = window.location.hostname;
    return host === 'localhost' || host === '127.0.0.1' || host.startsWith('192.168.');
  };

  const loadQueue = () => {
    try {
      const raw = window.localStorage.getItem(KEY);
      if (!raw) return [];
      const parsed = JSON.parse(raw);
      return Array.isArray(parsed) ? parsed : [];
    } catch { return []; }
  };

  // Cover queue: one entry per slug (new selection replaces old).
  const loadCoverQueue = () => {
    try {
      const raw = window.localStorage.getItem(COVER_KEY);
      if (!raw) return [];
      const parsed = JSON.parse(raw);
      return Array.isArray(parsed) ? parsed : [];
    } catch { return []; }
  };

  const EVENT = 'dev-archive-queue:change';
  const COVER_EVENT = 'dev-cover-queue:change';

  const saveQueue = (q) => {
    try { window.localStorage.setItem(KEY, JSON.stringify(q)); } catch {}
    // Notify every other useDevQueue() instance in the page. localStorage's
    // own 'storage' event only fires in OTHER tabs (not the same tab), so
    // we need our own.
    window.dispatchEvent(new CustomEvent(EVENT));
  };

  const saveCoverQueue = (q) => {
    try { window.localStorage.setItem(COVER_KEY, JSON.stringify(q)); } catch {}
    window.dispatchEvent(new CustomEvent(COVER_EVENT));
  };

  // Hook — every instance stays in sync by listening to the EVENT above.
  // Without this, the × button and the widget have independent state:
  // × writes to localStorage, widget state never updates.
  const useDevQueue = () => {
    const [items, setItems] = React.useState(loadQueue);
    React.useEffect(() => {
      const sync = () => setItems(loadQueue());
      window.addEventListener(EVENT, sync);
      // Also catch cross-tab updates via the native storage event.
      window.addEventListener('storage', (e) => {
        if (e.key === KEY) sync();
      });
      return () => window.removeEventListener(EVENT, sync);
    }, []);
    const add = React.useCallback((slug, file) => {
      const cur = loadQueue();
      if (cur.some((p) => p.slug === slug && p.file === file)) return;
      saveQueue([...cur, { slug, file, queued_at: new Date().toISOString() }]);
    }, []);
    const remove = React.useCallback((slug, file) => {
      saveQueue(loadQueue().filter((p) => !(p.slug === slug && p.file === file)));
    }, []);
    const clear = React.useCallback(() => { saveQueue([]); }, []);
    const has = React.useCallback(
      (slug, file) => items.some((p) => p.slug === slug && p.file === file),
      [items]
    );
    return { items, add, remove, clear, has };
  };

  // Cover queue hook — one entry per slug. setCover(slug, file) replaces
  // any existing entry for that slug. Separate localStorage key so cover
  // picks don't mingle with archive queue.
  const useCoverQueue = () => {
    const [items, setItems] = React.useState(loadCoverQueue);
    React.useEffect(() => {
      const sync = () => setItems(loadCoverQueue());
      window.addEventListener(COVER_EVENT, sync);
      window.addEventListener('storage', (e) => {
        if (e.key === COVER_KEY) sync();
      });
      return () => window.removeEventListener(COVER_EVENT, sync);
    }, []);
    const setCover = React.useCallback((slug, file) => {
      const cur = loadCoverQueue().filter((p) => p.slug !== slug);
      saveCoverQueue([...cur, { slug, file, marked_at: new Date().toISOString() }]);
    }, []);
    const remove = React.useCallback((slug) => {
      saveCoverQueue(loadCoverQueue().filter((p) => p.slug !== slug));
    }, []);
    const clear = React.useCallback(() => { saveCoverQueue([]); }, []);
    const forSlug = React.useCallback(
      (slug) => items.find((p) => p.slug === slug) || null,
      [items]
    );
    return { items, setCover, remove, clear, forSlug };
  };

  // Parse slug + filename from a media.src URL. Videos live under /videos/,
  // photos under /<slug>/<file>. Sentinel slug __videos__ is used for video
  // items in both archive and cover flows.
  const parseMedia = (media) => {
    if (!media || !media.src || media.src.startsWith('linear-')) return null;
    try {
      const u = new URL(media.src);
      const parts = u.pathname.split('/').filter(Boolean);
      if (parts[0] === 'videos' && parts.length === 2) {
        return { slug: '__videos__', file: parts[1] };
      }
      if (parts.length >= 2) {
        return { slug: parts[0], file: parts[parts.length - 1] };
      }
    } catch { /* fall through */ }
    return null;
  };

  // × + ★ buttons — rendered inside SitPage stage HUD. No-op outside dev.
  // × queues this media for archival. ★ sets it as the sit's cover photo
  // (replaces any prior cover for that slug).
  window.DevArchiveButton = ({ sit, media }) => {
    if (!isDevMode()) return null;
    const parsed = parseMedia(media);
    if (!parsed) return null;
    const { slug, file } = parsed;

    const q = useDevQueue();
    const cq = useCoverQueue();
    const queued = q.has(slug, file);

    const handleArchiveClick = (e) => {
      e.stopPropagation();
      e.preventDefault();
      if (queued) q.remove(slug, file);
      else q.add(slug, file);
    };

    // Cover always uses sit.slug (the real slug), not the parsed slug
    // from the URL — that way videos (which live under /videos/ in R2)
    // still attach to their owning sit instead of the __videos__ sentinel.
    const coverSlug = sit ? sit.slug : slug;
    const existingCoverForSit = cq.forSlug(coverSlug);
    const isCoverForSit = existingCoverForSit && existingCoverForSit.file === file;
    const handleCoverClick = (e) => {
      e.stopPropagation();
      e.preventDefault();
      if (!coverSlug) return;
      if (isCoverForSit) cq.remove(coverSlug);
      else cq.setCover(coverSlug, file);
    };

    const archiveBtn = React.createElement('button', {
      type: 'button',
      key: 'archive',
      className: `dev-archive-btn${queued ? ' is-queued' : ''}`,
      onClick: handleArchiveClick,
      title: queued ? `Un-queue ${file}` : `Archive ${file}`,
      'aria-label': queued ? 'Un-queue for archive' : 'Queue for archive',
    }, queued ? '↺' : '×');

    const coverBtn = !coverSlug ? null : React.createElement('button', {
      type: 'button',
      key: 'cover',
      className: `dev-cover-btn${isCoverForSit ? ' is-active' : ''}`,
      onClick: handleCoverClick,
      title: isCoverForSit
        ? `Clear cover for ${coverSlug} (currently ${file})`
        : `Set as cover for ${coverSlug}${existingCoverForSit ? ` (replaces ${existingCoverForSit.file})` : ''}`,
      'aria-label': isCoverForSit ? 'Clear cover selection' : 'Set as sit cover',
    }, '★');

    return React.createElement('div', { className: 'dev-btn-toolbar' },
      coverBtn, archiveBtn
    );
  };

  // Widget — fixed bottom-right card. Shows count, Export, Clear.
  const DevArchiveWidget = () => {
    if (!isDevMode()) return null;
    const q = useDevQueue();
    const cq = useCoverQueue();
    const [open, setOpen] = React.useState(false);
    const count = q.items.length;
    const coverCount = cq.items.length;

    const downloadJson = (filename, payload) => {
      const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(url);
    };

    const handleExport = () => {
      downloadJson(`archive-queue-${Date.now()}.json`, {
        exported_at: new Date().toISOString(),
        items: q.items,
      });
    };

    const handleCoverExport = () => {
      downloadJson(`cover-queue-${Date.now()}.json`, {
        exported_at: new Date().toISOString(),
        items: cq.items,
      });
    };

    const handleClear = () => {
      if (!count) return;
      if (window.confirm(`Clear ${count} queued item${count === 1 ? '' : 's'}?`)) q.clear();
    };

    // Purge archive: copies the rclone command to clipboard. User pastes
    // in PowerShell to hard-delete the _archive/ prefix. One more step
    // keeps this operation out of the browser (browser has no R2 creds).
    const [purgeHint, setPurgeHint] = React.useState(null);
    const handlePurge = async () => {
      const cmd = 'rclone purge r2:dylan-agema-photos/_archive';
      try {
        await navigator.clipboard.writeText(cmd);
        setPurgeHint('command copied — paste in PowerShell');
      } catch {
        setPurgeHint(cmd);
      }
      setTimeout(() => setPurgeHint(null), 6000);
    };

    const toggleLabel = coverCount > 0
      ? `dev · ${count} archive · ${coverCount} cover`
      : `dev · ${count} queued`;

    return React.createElement('div', { className: 'dev-archive-widget' },
      React.createElement('button', {
        type: 'button',
        className: 'dev-archive-widget__toggle mono-caps',
        onClick: () => setOpen(!open),
      }, toggleLabel),
      open && React.createElement('div', { className: 'dev-archive-widget__panel' },
        count === 0
          ? React.createElement('div', { className: 'dev-archive-widget__empty mono-caps' },
              'no items queued — click × on media to archive')
          : React.createElement('ul', { className: 'dev-archive-widget__list' },
              q.items.slice(-8).reverse().map((it) =>
                React.createElement('li', { key: `${it.slug}/${it.file}`, className: 'mono-caps' },
                  React.createElement('span', null, `${it.slug}/${it.file}`),
                  React.createElement('button', {
                    type: 'button',
                    className: 'dev-archive-widget__undo',
                    onClick: () => q.remove(it.slug, it.file),
                    title: 'Undo',
                  }, '↺')
                )
              )),
        React.createElement('div', { className: 'dev-archive-widget__actions' },
          React.createElement('button', {
            type: 'button',
            className: 'dev-archive-widget__btn mono-caps',
            onClick: handleExport,
            disabled: !count,
          }, 'export'),
          React.createElement('button', {
            type: 'button',
            className: 'dev-archive-widget__btn dev-archive-widget__btn--danger mono-caps',
            onClick: handleClear,
            disabled: !count,
          }, 'clear')
        ),
        React.createElement('div', { className: 'dev-archive-widget__divider' }),
        React.createElement('div', { className: 'dev-archive-widget__section mono-caps' },
          `covers · ${coverCount}`),
        coverCount > 0 && React.createElement('ul', { className: 'dev-archive-widget__list' },
          cq.items.slice(0, 6).map((it) =>
            React.createElement('li', { key: it.slug, className: 'mono-caps' },
              React.createElement('span', null, `${it.slug} ← ${it.file}`),
              React.createElement('button', {
                type: 'button',
                className: 'dev-archive-widget__undo',
                onClick: () => cq.remove(it.slug),
                title: 'Clear cover selection',
              }, '↺')
            )
          )),
        React.createElement('div', { className: 'dev-archive-widget__actions' },
          React.createElement('button', {
            type: 'button',
            className: 'dev-archive-widget__btn mono-caps',
            onClick: handleCoverExport,
            disabled: !coverCount,
          }, 'export covers'),
          React.createElement('button', {
            type: 'button',
            className: 'dev-archive-widget__btn dev-archive-widget__btn--danger mono-caps',
            onClick: () => { if (coverCount && window.confirm(`Clear ${coverCount} cover mark${coverCount===1?'':'s'}?`)) cq.clear(); },
            disabled: !coverCount,
          }, 'clear covers')
        ),
        React.createElement('div', { className: 'dev-archive-widget__divider' }),
        React.createElement('button', {
          type: 'button',
          className: 'dev-archive-widget__btn dev-archive-widget__btn--danger mono-caps',
          style: { width: '100%' },
          onClick: handlePurge,
          title: 'Copy the rclone command that hard-deletes the _archive/ prefix in R2',
        }, 'purge archive'),
        purgeHint && React.createElement('div', { className: 'dev-archive-widget__hint mono-caps' }, purgeHint)
      )
    );
  };

  window.DevArchiveWidget = DevArchiveWidget;
})();
