// Florio Analytics — Gaps tab (real data via window.FlorioDB).
// Ported from js/pages/gaps.js. Lists labeling_queue items flagged status:"no_match"
// — photos where no close-matching avatar exists. These drive which new avatars to design.
// Range prop is ignored (this page is not date-range based).
(function () {
  const NS = window.HyperChartsDesignSystem_e51a95;
  const { Badge } = NS;
  const I = window.HyperIcons;
  const { useState, useEffect, useCallback } = React;

  // Lazy Firestore accessor — window.FlorioDB is only present after FlorioReady.
  const FB = () => window.FlorioDB;

  function Spinner() {
    return (
      <div style={{ display: 'grid', placeItems: 'center', minHeight: '60vh' }}>
        <div className="pa-spin" style={{ width: 38, height: 38, borderRadius: '50%', border: '3px solid var(--border-default)', borderTopColor: 'var(--app-accent)' }} />
      </div>
    );
  }

  function EmptyState({ title, note }) {
    return (
      <div style={{ minHeight: 220, display: 'grid', placeItems: 'center', textAlign: 'center', gap: 10, padding: 32 }}>
        <span style={{ color: 'var(--text-muted)', display: 'flex' }}>{I.sparkle ? I.sparkle({ size: 30 }) : null}</span>
        <span style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 16, color: 'var(--text-secondary)' }}>{title}</span>
        {note && <span style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)', maxWidth: 360, lineHeight: 1.45 }}>{note}</span>}
      </div>
    );
  }

  function GapCard({ item, onReopen, onDelete }) {
    const [busy, setBusy] = useState(false);
    const [imgErr, setImgErr] = useState(false);

    const run = async (fn) => {
      if (busy) return;
      setBusy(true);
      try { await fn(); } finally { setBusy(false); }
    };

    return (
      <div style={{
        background: 'var(--surface-card)', border: '1px solid var(--border-subtle)',
        borderRadius: 'var(--radius-2xl)', boxShadow: 'var(--shadow-card)',
        overflow: 'hidden', display: 'flex', flexDirection: 'column', minWidth: 0,
        opacity: busy ? 0.55 : 1, transition: 'opacity .15s',
      }}>
        <div style={{ aspectRatio: '1 / 1', background: 'var(--surface-sunken)', display: 'grid', placeItems: 'center', overflow: 'hidden' }}>
          {item.photoUrl && !imgErr
            ? <img src={item.photoUrl} alt={item.species || 'plant'} onError={() => setImgErr(true)}
                style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
            : <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--text-muted)' }}>image unavailable</span>}
        </div>
        <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 8, flex: 1 }}>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4, flex: 1 }}>
            <span style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 14, color: 'var(--text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
              {item.species || 'unknown species'}
            </span>
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 10.5, letterSpacing: '.04em', color: 'var(--text-muted)' }}>
              source: {item.source || 'manual'}
            </span>
          </div>
          <div style={{ display: 'flex', gap: 8, marginTop: 4 }}>
            <button onClick={() => run(() => onReopen(item.id))} disabled={busy}
              style={{ flex: 1, padding: '8px 10px', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', background: 'var(--surface-card)', cursor: busy ? 'default' : 'pointer', fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 12, color: 'var(--text-secondary)' }}>
              Send back to labeling
            </button>
            <button onClick={() => { if (window.confirm('Delete this photo from the queue entirely?')) run(() => onDelete(item.id)); }} disabled={busy}
              style={{ padding: '8px 12px', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', background: 'var(--surface-card)', cursor: busy ? 'default' : 'pointer', fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 12, color: 'var(--negative)' }}>
              Delete
            </button>
          </div>
        </div>
      </div>
    );
  }

  function Gaps({ range }) {
    const [items, setItems] = useState(null); // null = loading
    const [error, setError] = useState(null);

    useEffect(() => {
      let alive = true;
      (async () => {
        try {
          await window.FlorioReady;
          const { db, collection, query, where, getDocs } = FB();
          const q = query(collection(db, 'labeling_queue'), where('status', '==', 'no_match'));
          const snap = await getDocs(q);
          const list = [];
          snap.forEach((d) => list.push(Object.assign({ id: d.id }, d.data())));
          // Most recently flagged first.
          list.sort((a, b) => (b.resolvedAt?.seconds || 0) - (a.resolvedAt?.seconds || 0));
          if (alive) setItems(list);
        } catch (e) {
          console.error('[Gaps] load failed:', e);
          if (alive) { setError(e.message || String(e)); setItems([]); }
        }
      })();
      return () => { alive = false; };
    }, []);

    const reopen = useCallback(async (id) => {
      const { db, doc, setDoc } = FB();
      try {
        await setDoc(doc(db, 'labeling_queue', id), { status: 'pending', resolvedAt: null }, { merge: true });
        setItems((prev) => (prev || []).filter((it) => it.id !== id));
      } catch (e) {
        console.error('[Gaps] reopen failed:', e);
        window.alert('Reopen failed: ' + (e.message || e));
      }
    }, []);

    const remove = useCallback(async (id) => {
      const { db, doc, deleteDoc } = FB();
      try {
        await deleteDoc(doc(db, 'labeling_queue', id));
        setItems((prev) => (prev || []).filter((it) => it.id !== id));
      } catch (e) {
        console.error('[Gaps] delete failed:', e);
        window.alert('Delete failed: ' + (e.message || e));
      }
    }, []);

    if (items === null) return <Spinner />;

    const count = items.length;

    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <span style={{ width: 40, height: 40, borderRadius: 11, display: 'grid', placeItems: 'center', background: 'color-mix(in srgb, var(--app-accent) 14%, transparent)', color: 'var(--app-accent)' }}>
            {I.search ? I.search({ size: 20 }) : null}
          </span>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 18, color: 'var(--text-primary)' }}>Avatar catalog gaps</div>
            <div style={{ fontFamily: 'var(--font-sans)', fontSize: 12.5, color: 'var(--text-muted)' }}>
              Photos flagged "no close matching avatar" — the highest-signal candidates for new avatar designs.
            </div>
          </div>
          {Badge
            ? <Badge>{count} {count === 1 ? 'gap' : 'gaps'}</Badge>
            : <span style={{ fontFamily: 'var(--font-mono)', fontWeight: 700, fontSize: 14, color: 'var(--text-primary)' }}>{count}</span>}
        </div>

        {error
          ? <div style={{ background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-2xl)', boxShadow: 'var(--shadow-card)' }}>
              <EmptyState title="Failed to load" note={error} />
            </div>
          : count === 0
            ? <div style={{ background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-2xl)', boxShadow: 'var(--shadow-card)' }}>
                <EmptyState
                  title="No gap reports yet"
                  note={'When you flag a plant as "No close matching avatars" on the labeling page, it shows up here.'} />
              </div>
            : <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 18 }}>
                {items.map((item) => (
                  <GapCard key={item.id} item={item} onReopen={reopen} onDelete={remove} />
                ))}
              </div>}
      </div>
    );
  }

  (window.FlorioTabs = window.FlorioTabs || {}).gaps = Gaps;
})();
