/* global React, Icons, Field, Select, NumPair, RatingRange, Toggle, ProductImage, Sparkline, Stars, Checkbox, Segmented, MASTER_COLUMNS, COLUMN_BY_KEY, renderCellValue, ManageColumnsDrawer */
const { useState, useMemo } = React;

const SEARCH_DEFAULT_ORDER = [
  'product','asin','category','price','revenue30d','monthlySales','bsr','reviews','rating','videos','listingAge','variations','cc','campaignDate','budget','remainingBudget','onSite','onSiteCommission','ccCommission','trend',
];

const KEEPA_KEY_STORAGE   = 'keepa_api_key';
const CC_IMPORT_STORAGE   = 'aip_cc_import';
const SAVED_SEARCHES_KEY  = 'aip_saved_searches';

function loadSavedSearches() {
  try { return JSON.parse(localStorage.getItem(SAVED_SEARCHES_KEY) || '[]'); }
  catch { return []; }
}

/* Amazon US root category → Keepa node ID */
const CATEGORY_NODES = {
  'Appliances':                2619525011,
  'Arts, Crafts & Sewing':     2617941011,
  'Automotive':                15684181,
  'Baby Products':             165796011,
  'Beauty & Personal Care':    3760911,
  'Books':                     283155,
  'Cell Phones & Accessories': 2335752011,
  'Clothing, Shoes & Jewelry': 7141123011,
  'Electronics':               172282,
  'Grocery & Gourmet Food':    16310101,
  'Health & Household':        3760901,
  'Home & Kitchen':            1055398,
  'Industrial & Scientific':   16310091,
  'Kindle Store':              133140011,
  'Movies & TV':               2625373011,
  'Musical Instruments':       11091801,
  'Office Products':           1064954,
  'Patio, Lawn & Garden':      2972638011,
  'Pet Supplies':              2619533011,
  'Software':                  229534,
  'Sports & Outdoors':         3375251,
  'Tools & Home Improvement':  228013,
  'Toys & Games':              165793011,
  'Video Games':               468642,
};

/* ── small seeded RNG for sparkline generation ─────────────────── */
function miniRng(seed) {
  let s = seed;
  return () => { s = (s * 9301 + 49297) % 233280; return s / 233280; };
}

function FilterSection({ title, open, onToggle, count, children }) {
  return (
    <div style={{ borderTop: '1px solid var(--line)' }}>
      <div
        onClick={onToggle}
        style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 20px', cursor: 'pointer', userSelect: 'none' }}
      >
        <span style={{
          display: 'inline-flex', width: 18, height: 18, alignItems: 'center', justifyContent: 'center',
          transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: 'transform 0.18s',
          color: 'var(--text-2)',
        }}>
          <Icons.chevDown size={13}/>
        </span>
        <div style={{ fontSize: 13, fontWeight: 600 }}>{title}</div>
        <span style={{ fontSize: 11, color: 'var(--text-3)', fontFamily: 'var(--font-mono)' }}>{count} filters</span>
      </div>
      {open && (
        <div style={{ padding: '4px 20px 18px', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
          {children}
        </div>
      )}
    </div>
  );
}

const SearchPage = ({ leads, setLeads }) => {
  const { CATEGORIES, fmtUSD, fmtInt, fmtBSR } = window.AIP_DATA;

  /* ── filter state ────────────────────────────────────────────── */
  const [filters, setFilters] = useState({
    title: '', category: 'All categories',
    listingAge: ['', ''], price: ['', '500'], monthlySales: ['', ''],
    revenue30d: ['', ''], bsr: ['', ''], reviewCount: ['', ''],
    rating: [3.5, 5.0], variations: ['', ''], videos: ['', ''],
    creator: false,
    campaignStart: ['', ''], campaignEnd: ['', ''],
    campaignPct: ['', ''], campaignBudget: ['', ''], remainingBudget: ['', ''],
  });

  /* ── Keepa API state ─────────────────────────────────────────── */
  const [apiKey,       setApiKey]       = useState(() => localStorage.getItem(KEEPA_KEY_STORAGE) || '');
  const [keyDraft,     setKeyDraft]     = useState('');
  const [showKeyInput, setShowKeyInput] = useState(false);
  const [results,      setResults]      = useState(null);   // null=not searched
  const [searching,    setSearching]    = useState(false);
  const [searchError,  setSearchError]  = useState(null);
  const [tokensLeft,   setTokensLeft]   = useState(null);
  const [totalMatches, setTotalMatches] = useState(null);

  /* ── saved searches state ────────────────────────────────────── */
  const [savedSearches,   setSavedSearches]   = useState(() => loadSavedSearches());
  const [showSavedPanel,  setShowSavedPanel]  = useState(false);
  const [saveName,        setSaveName]        = useState('');

  /* ── table / view state ──────────────────────────────────────── */
  const [selected,      setSelected]      = useState(new Set());
  const [sort,          setSort]          = useState({ key: 'bsr', dir: 'asc' });
  const [view,          setView]          = useState('table');
  const [showAddPicker, setShowAddPicker] = useState(false);
  const [showColumns,   setShowColumns]   = useState(false);
  const [customCols,    setCustomCols]    = useState([]);
  const [order,         setOrder]         = useState(SEARCH_DEFAULT_ORDER);
  const [hidden,        setHidden]        = useState(new Set());
  const [notes,         setNotes]         = useState({});

  /* ── collapsible sections ────────────────────────────────────── */
  const [open, setOpen] = useState({ basics: true, performance: true, listing: true, creator: false });
  const toggleSec = (k) => setOpen(o => ({ ...o, [k]: !o[k] }));

  /* ── helpers ─────────────────────────────────────────────────── */
  const updateNote  = (pid, colKey, text) => setNotes(prev => ({ ...prev, [pid]: { ...(prev[pid] || {}), [colKey]: text } }));
  const isCustomKey = (k) => k.startsWith('cust_');
  const toNum       = (s) => s === '' ? null : parseFloat(s);

  /* ── API key save ────────────────────────────────────────────── */
  const confirmApiKey = () => {
    const k = keyDraft.trim();
    setApiKey(k);
    localStorage.setItem(KEEPA_KEY_STORAGE, k);
    setShowKeyInput(false);
    setKeyDraft('');
  };

  /* ── Keepa search ────────────────────────────────────────────── */
  async function runKeepaSearch() {
    if (!apiKey.trim()) {
      setKeyDraft('');
      setShowKeyInput(true);
      return;
    }
    setSearching(true);
    setSearchError(null);
    setResults(null);

    try {
      const key = apiKey.trim();
      let asinList = [];

      /* ── Direct ASIN lookup (10-char alphanumeric) ─────────────── */
      const titleTrimmed = filters.title.trim();
      const isAsin = /^[A-Z0-9]{10}$/i.test(titleTrimmed);

      if (isAsin) {
        asinList = [titleTrimmed.toUpperCase()];
        setTotalMatches(1);
      } else {
        /* ── Product Finder (costs ~11 tokens) ─────────────────────── */
        const sel = {};

        if (titleTrimmed)                    sel.title                     = titleTrimmed;

        const catNode = CATEGORY_NODES[filters.category];
        if (catNode)                         sel.rootCategory              = catNode;

        if (toNum(filters.price[0]))         sel.current_NEW_gte           = Math.round(toNum(filters.price[0]) * 100);
        if (toNum(filters.price[1]))         sel.current_NEW_lte           = Math.round(toNum(filters.price[1]) * 100);

        if (toNum(filters.bsr[0]))           sel.current_SALES_gte         = toNum(filters.bsr[0]);
        if (toNum(filters.bsr[1]))           sel.current_SALES_lte         = toNum(filters.bsr[1]);

        if (toNum(filters.monthlySales[0]))  sel.monthlySold_gte           = toNum(filters.monthlySales[0]);
        if (toNum(filters.monthlySales[1]))  sel.monthlySold_lte           = toNum(filters.monthlySales[1]);

        if (toNum(filters.reviewCount[0]))   sel.current_COUNT_REVIEWS_gte = toNum(filters.reviewCount[0]);
        if (toNum(filters.reviewCount[1]))   sel.current_COUNT_REVIEWS_lte = toNum(filters.reviewCount[1]);

        if (filters.rating[0] > 0)           sel.current_RATING_gte        = Math.round(filters.rating[0] * 10);
        if (filters.rating[1] < 5)           sel.current_RATING_lte        = Math.round(filters.rating[1] * 10);

        const keepaNow = Math.floor(Date.now() / 60000) - 21564000;
        if (toNum(filters.listingAge[0]))    sel.listedSince_lte           = keepaNow - Math.round(toNum(filters.listingAge[0]) * 1440);
        if (toNum(filters.listingAge[1]))    sel.listedSince_gte           = keepaNow - Math.round(toNum(filters.listingAge[1]) * 1440);

        if (toNum(filters.videos[0]))        sel.videoCount_gte            = toNum(filters.videos[0]);
        if (toNum(filters.videos[1]))        sel.videoCount_lte            = toNum(filters.videos[1]);

        sel.perPage = 50;
        sel.page    = 0;

        const qRes  = await fetch(`https://api.keepa.com/query?key=${key}&domain=1&selection=${encodeURIComponent(JSON.stringify(sel))}`);
        const qData = await qRes.json();
        if (qData.error) throw new Error(qData.error.message || 'Keepa query error');

        asinList = (qData.asinList || []).slice(0, 50);
        setTotalMatches(qData.totalResults ?? null);
        setTokensLeft(qData.tokensLeft ?? null);

        if (asinList.length === 0) {
          setResults([]);
          setSearching(false);
          return;
        }
      }

      /* ── Product detail fetch (costs ~3 tokens per product) ─────── */
      const pRes  = await fetch(`https://api.keepa.com/product?key=${key}&domain=1&asin=${asinList.join(',')}&stats=180&rating=1&videos=1`);
      const pData = await pRes.json();
      if (pData.error) throw new Error(pData.error.message || 'Keepa product error');
      setTokensLeft(pData.tokensLeft ?? null);

      /* Step 3 — Load CC overlay data from IndexedDB (keyed by ASIN) */
      const ccMap = {};
      try {
        const db = await new Promise((resolve, reject) => {
          const req = indexedDB.open('aip_db', 1);
          req.onupgradeneeded = e => {
            if (!e.target.result.objectStoreNames.contains('cc_records')) {
              e.target.result.createObjectStore('cc_records', { keyPath: 'asin' });
            }
          };
          req.onsuccess = e => resolve(e.target.result);
          req.onerror   = e => reject(e.target.error);
        });
        const tx    = db.transaction('cc_records', 'readonly');
        const store = tx.objectStore('cc_records');
        await Promise.all(asinList.map(asin => new Promise(resolve => {
          const req = store.get(asin);
          req.onsuccess = e => { if (e.target.result) ccMap[asin] = e.target.result; resolve(); };
          req.onerror   = () => resolve();
        })));
      } catch (_) {}

      /* Step 4 — Map Keepa fields to product format */
      /* Keepa minutes epoch for listing age calculation */
      const keepaNowProd = Math.floor(Date.now() / 60000) - 21564000;

      /* Last value in a Keepa CSV array [ts, val, ts, val, …] */
      const csvCurrent = (csv, idx) => {
        const arr = csv?.[idx];
        if (!Array.isArray(arr) || arr.length < 2) return 0;
        const v = arr[arr.length - 1];
        return v > 0 ? v : 0;
      };

      const products = (pData.products || []).map((kp, i) => {
        const cur = kp.stats?.current || [];

        /* Price — new/marketplace (cur[1]) first, then buy box, FBA, Amazon */
        const rawPrice = [1, 18, 10, 0].reduce((acc, idx) => acc > 0 ? acc : (cur[idx] > 0 ? cur[idx] : 0), 0);
        const price     = rawPrice / 100;

        const bsr       = cur[3] > 0 ? cur[3] : 0;

        /* Rating (csv[16] = rating×10) and review count (csv[17]), fall back to stats if CSV absent */
        const ratingRaw = csvCurrent(kp.csv, 16) || (cur[16] > 0 ? cur[16] : 0);
        const reviews   = csvCurrent(kp.csv, 17) || (cur[17] > 0 ? cur[17] : 0);
        const rating    = ratingRaw ? +(ratingRaw / 10).toFixed(1) : 0;
        const cost      = +(price * 0.55).toFixed(2);
        const profit    = +(price * 0.18).toFixed(2);
        const roi       = cost > 0 ? Math.round((profit / cost) * 100) : 0;
        const monthly   = kp.monthlySold || 0;

        /* Root category = first entry in the tree */
        const catName   = kp.categoryTree?.[0]?.name || 'Unknown';

        /* Listing age in days — Keepa stores as minutes since Jan 1 2011 */
        const listingAge = kp.listedSince > 0 ? Math.floor((keepaNowProd - kp.listedSince) / 1440) : 0;

        /* Product image — images[0] is an object; use medium (m) or large (l) filename */
        const imgObj = kp.images?.[0] || null;
        const image  = imgObj?.m || imgObj?.l || null; // filename already includes extension

        const cc    = ccMap[kp.asin];
        const ccPct = cc ? +cc.commission : 0;
        const r     = miniRng(i * 1337 + 7);
        const trend = Array.from({ length: 14 }, () => Math.max(5, 40 + (r() - 0.5) * 40));

        return {
          id:              `k${i}_${kp.asin}`,
          name:            kp.title || kp.asin,
          asin:            kp.asin,
          brand:           kp.brand || '—',
          category:        catName,
          source:          'Keepa',
          image,
          cost, price, profit, roi,
          monthlySales:    monthly,
          revenue30d:      monthly > 0 ? Math.round(monthly * price) : null,
          bsr,
          reviews,
          rating,
          variations:      kp.variations?.length || 1,
          videos:          kp.videos != null ? kp.videos.length : null,
          cc:              ccPct,
          onSite:          0,
          onSiteCommission: 0,
          ccCommission:    ccPct > 0 ? +(price * (ccPct / 100)).toFixed(2) : 0,
          listingAge,
          budget:          cc?.budgetRemaining || 0,
          remainingBudget: cc?.budgetRemaining || 0,
          campaignStart:   cc?.startDate  || null,
          campaignEnd:     cc?.endDate    || null,
          trend,
          addedDate:       '',
          list:            '',
          inLeads:         false,
          hasCC:           !!cc,
        };
      });

      /* Post-filter for fields Keepa's Product Finder doesn't natively support */
      const filtered = products.filter(p => {
        // Variations + revenue — not available in Keepa's Product Finder
        if (toNum(filters.variations[0]) !== null && p.variations < toNum(filters.variations[0])) return false;
        if (toNum(filters.variations[1]) !== null && p.variations > toNum(filters.variations[1])) return false;
        if (toNum(filters.revenue30d[0]) !== null && (p.revenue30d ?? 0) < toNum(filters.revenue30d[0])) return false;
        if (toNum(filters.revenue30d[1]) !== null && (p.revenue30d ?? 0) > toNum(filters.revenue30d[1])) return false;

        // Creator Connections — only applied when the CC toggle is on
        if (filters.creator) {
          if (!p.hasCC) return false;
          if (toNum(filters.campaignPct[0]) !== null && p.cc < toNum(filters.campaignPct[0])) return false;
          if (toNum(filters.campaignPct[1]) !== null && p.cc > toNum(filters.campaignPct[1])) return false;
          if (toNum(filters.campaignBudget[0]) !== null && p.remainingBudget < toNum(filters.campaignBudget[0])) return false;
          if (toNum(filters.campaignBudget[1]) !== null && p.remainingBudget > toNum(filters.campaignBudget[1])) return false;
          if (toNum(filters.remainingBudget[0]) !== null && p.remainingBudget < toNum(filters.remainingBudget[0])) return false;
          if (toNum(filters.remainingBudget[1]) !== null && p.remainingBudget > toNum(filters.remainingBudget[1])) return false;
          if (filters.campaignStart[0] && p.campaignStart && p.campaignStart < filters.campaignStart[0]) return false;
          if (filters.campaignStart[1] && p.campaignStart && p.campaignStart > filters.campaignStart[1]) return false;
          if (filters.campaignEnd[0] && p.campaignEnd && p.campaignEnd < filters.campaignEnd[0]) return false;
          if (filters.campaignEnd[1] && p.campaignEnd && p.campaignEnd > filters.campaignEnd[1]) return false;
        }

        return true;
      });

      setResults(filtered);
    } catch (err) {
      setSearchError(err.message);
    } finally {
      setSearching(false);
    }
  }

  /* ── sorted results ──────────────────────────────────────────── */
  const sorted = useMemo(() => {
    if (!results) return [];
    return [...results].sort((a, b) => {
      const va = a[sort.key], vb = b[sort.key];
      if (typeof va === 'number') return sort.dir === 'asc' ? va - vb : vb - va;
      return sort.dir === 'asc' ? String(va).localeCompare(String(vb)) : String(vb).localeCompare(String(va));
    });
  }, [results, sort]);

  const toggleSel  = (id) => setSelected(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const toggleAll  = () => setSelected(prev => prev.size === sorted.length ? new Set() : new Set(sorted.map(p => p.id)));
  const setSortKey = (key) => setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'desc' });

  const reset = () => setFilters({
    title: '', category: 'All categories', listingAge: ['', ''], price: ['', '500'], monthlySales: ['', ''],
    revenue30d: ['', ''], bsr: ['', ''], reviewCount: ['', ''], rating: [3.5, 5.0],
    variations: ['', ''], videos: ['', ''], creator: false,
    campaignStart: ['', ''], campaignEnd: ['', ''],
    campaignPct: ['', ''], campaignBudget: ['', ''], remainingBudget: ['', ''],
  });

  /* ── saved search helpers ────────────────────────────────────── */
  const saveCurrentSearch = () => {
    if (!saveName.trim()) return;
    const updated = [...savedSearches, { id: Date.now(), name: saveName.trim(), filters }];
    setSavedSearches(updated);
    localStorage.setItem(SAVED_SEARCHES_KEY, JSON.stringify(updated));
    setSaveName('');
  };
  const loadSearch = (entry) => {
    setFilters(entry.filters);
    setShowSavedPanel(false);
  };
  const deleteSearch = (id) => {
    const updated = savedSearches.filter(s => s.id !== id);
    setSavedSearches(updated);
    localStorage.setItem(SAVED_SEARCHES_KEY, JSON.stringify(updated));
  };

  const visibleCols = order.filter(k => !hidden.has(k));

  const addSelectedToList = (listId) => {
    const ids = [...selected];
    setLeads(prev => ({
      ...prev,
      products: [
        ...prev.products,
        ...ids.filter(id => !prev.products.some(x => x.id === id)).map(id => {
          const p = results.find(pp => pp.id === id);
          return { ...p, list: listId, addedDate: new Date().toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) };
        }),
      ],
    }));
    setSelected(new Set()); setShowAddPicker(false);
  };

  /* ── render ──────────────────────────────────────────────────── */
  return (
    <div className="page">
      <div className="page-header">
        <div className="page-title-block">
          <h1>Search</h1>
          <div className="subtitle">Filter by title, category, price, BSR, sales, reviews, and rating — results come from Keepa, enriched with your CC data.</div>
        </div>
        <div className="page-actions">
          {/* Saved searches */}
          <div style={{ position: 'relative' }}>
            <button className="btn btn-ghost" onClick={() => setShowSavedPanel(s => !s)}>
              <Icons.bookmark size={14}/>
              Saved searches
              {savedSearches.length > 0 && (
                <span className="badge badge-neutral" style={{ marginLeft: 4, fontSize: 10 }}>{savedSearches.length}</span>
              )}
            </button>

            {showSavedPanel && (
              <>
                <div
                  style={{ position: 'fixed', inset: 0, zIndex: 19 }}
                  onClick={() => setShowSavedPanel(false)}
                />
                <div style={{
                  position: 'absolute', top: 'calc(100% + 8px)', right: 0, zIndex: 20,
                  background: 'var(--bg-elev)', border: '1px solid var(--line-strong)',
                  borderRadius: 12, padding: 16, minWidth: 300,
                  boxShadow: '0 12px 40px -8px rgba(0,0,0,0.6)',
                }}>
                  {/* Save current filters */}
                  <div style={{ marginBottom: 14 }}>
                    <div className="card-eyebrow" style={{ marginBottom: 8 }}>Save current filters</div>
                    <div style={{ display: 'flex', gap: 8 }}>
                      <input
                        className="field-input"
                        placeholder="Name this search…"
                        value={saveName}
                        onChange={e => setSaveName(e.target.value)}
                        onKeyDown={e => e.key === 'Enter' && saveCurrentSearch()}
                        style={{ flex: 1 }}
                        autoFocus
                      />
                      <button
                        className="btn btn-primary btn-sm"
                        onClick={saveCurrentSearch}
                        disabled={!saveName.trim()}
                      >
                        Save
                      </button>
                    </div>
                  </div>

                  {/* Saved list */}
                  {savedSearches.length > 0 && (
                    <>
                      <div className="card-eyebrow" style={{ marginBottom: 8 }}>Saved</div>
                      {savedSearches.map(s => (
                        <div
                          key={s.id}
                          onClick={() => loadSearch(s)}
                          style={{
                            display: 'flex', alignItems: 'center', gap: 10,
                            padding: '9px 12px', borderRadius: 8, marginBottom: 4,
                            background: 'var(--bg-2)', border: '1px solid var(--line)',
                            cursor: 'pointer', transition: 'background 0.12s',
                          }}
                          onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-3)'}
                          onMouseLeave={e => e.currentTarget.style.background = 'var(--bg-2)'}
                        >
                          <Icons.bookmark size={12} style={{ color: 'var(--accent)', flexShrink: 0 }}/>
                          <span style={{ flex: 1, fontSize: 13 }}>{s.name}</span>
                          <button
                            className="icon-btn"
                            style={{ width: 26, height: 26 }}
                            onClick={e => { e.stopPropagation(); deleteSearch(s.id); }}
                          >
                            <Icons.trash size={12}/>
                          </button>
                        </div>
                      ))}
                    </>
                  )}

                  {savedSearches.length === 0 && (
                    <div style={{ fontSize: 13, color: 'var(--text-3)', textAlign: 'center', padding: '4px 0 2px' }}>
                      No saved searches yet
                    </div>
                  )}
                </div>
              </>
            )}
          </div>

          <button className="btn"><Icons.upload size={14}/> Export</button>
        </div>
      </div>

      {/* API KEY BANNER — shown when no key is set or user wants to edit */}
      {(showKeyInput || !apiKey) && (
        <div className="card" style={{ padding: '16px 20px', marginBottom: 16, display: 'flex', alignItems: 'center', gap: 12, background: 'var(--accent-soft)', border: '1px solid var(--accent)' }}>
          <Icons.bolt size={16} style={{ color: 'var(--accent)', flexShrink: 0 }}/>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>Keepa API key required</div>
            <div style={{ display: 'flex', gap: 8 }}>
              <input
                className="field-input"
                style={{ flex: 1, maxWidth: 400, fontFamily: 'var(--font-mono)', fontSize: 12 }}
                placeholder="Paste your Keepa API key…"
                value={keyDraft}
                onChange={e => setKeyDraft(e.target.value)}
                onKeyDown={e => e.key === 'Enter' && confirmApiKey()}
                autoFocus
              />
              <button className="btn btn-primary" onClick={confirmApiKey} disabled={!keyDraft.trim()}>Save key</button>
              {apiKey && <button className="btn btn-ghost" onClick={() => setShowKeyInput(false)}>Cancel</button>}
            </div>
          </div>
        </div>
      )}

      {/* FILTERS */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '14px 20px', borderBottom: '1px solid var(--line)' }}>
          <Icons.filter size={14} style={{ color: 'var(--accent)' }}/>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Search filters</div>
          <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8 }}>
            {/* API key status */}
            {apiKey && !showKeyInput && (
              <button
                className="btn btn-sm btn-ghost"
                onClick={() => { setKeyDraft(''); setShowKeyInput(true); }}
                title="Change Keepa API key"
              >
                <Icons.bolt size={11} style={{ color: 'var(--success)' }}/> API key set
              </button>
            )}
            {/* Token balance */}
            {tokensLeft !== null && (
              <span style={{ fontSize: 11, color: 'var(--text-3)', fontFamily: 'var(--font-mono)' }}>
                {tokensLeft.toLocaleString()} tokens left
              </span>
            )}
            <button className="btn btn-sm btn-ghost" onClick={() => setOpen({ basics: true, performance: true, listing: true, creator: open.creator })}>Expand all</button>
            <button className="btn btn-sm btn-ghost" onClick={() => setOpen({ basics: false, performance: false, listing: false, creator: false })}>Collapse all</button>
            <button className="btn btn-sm btn-ghost" onClick={reset}><Icons.reset size={12}/> Reset</button>
            <button
              className="btn btn-primary"
              onClick={runKeepaSearch}
              disabled={searching}
              style={{ minWidth: 90 }}
            >
              {searching ? <><Icons.bolt size={13}/> Searching…</> : <><Icons.search size={13}/> Search</>}
            </button>
          </div>
        </div>

        {/* Basics */}
        <FilterSection title="Basics" open={open.basics} onToggle={() => toggleSec('basics')} count={4}>
          <Field label="Title search or ASIN lookup">
            <input className="field-input" placeholder="Keywords  —or—  paste an ASIN for direct lookup" value={filters.title}
              onChange={(e) => setFilters({ ...filters, title: e.target.value })} />
          </Field>
          <Field label="Category">
            <Select value={filters.category} onChange={(v) => setFilters({ ...filters, category: v })}
              options={['All categories', ...CATEGORIES]} />
          </Field>
          <Field label="Listing age (days)"><NumPair values={filters.listingAge} onChange={(v) => setFilters({ ...filters, listingAge: v })} /></Field>
          <Field label="Price ($)"><NumPair values={filters.price} onChange={(v) => setFilters({ ...filters, price: v })} /></Field>
        </FilterSection>

        {/* Performance */}
        <FilterSection title="Performance" open={open.performance} onToggle={() => toggleSec('performance')} count={4}>
          <Field label="Monthly sales"><NumPair values={filters.monthlySales} onChange={(v) => setFilters({ ...filters, monthlySales: v })} /></Field>
          <Field label="30‑day revenue ($)"><NumPair values={filters.revenue30d} onChange={(v) => setFilters({ ...filters, revenue30d: v })} /></Field>
          <Field label="Best Seller Rank"><NumPair values={filters.bsr} onChange={(v) => setFilters({ ...filters, bsr: v })} /></Field>
          <Field label="Review count"><NumPair values={filters.reviewCount} onChange={(v) => setFilters({ ...filters, reviewCount: v })} /></Field>
        </FilterSection>

        {/* Listing details */}
        <FilterSection title="Listing details" open={open.listing} onToggle={() => toggleSec('listing')} count={3}>
          <Field label="Number of variations"><NumPair values={filters.variations} onChange={(v) => setFilters({ ...filters, variations: v })} /></Field>
          <Field label="Video count"><NumPair values={filters.videos} onChange={(v) => setFilters({ ...filters, videos: v })} /></Field>
          <div className="field" style={{ gridColumn: 'span 2' }}>
            <label className="field-label">Rating ({filters.rating[0].toFixed(1)} – {filters.rating[1].toFixed(1)} ★)</label>
            <RatingRange value={filters.rating} onChange={(v) => setFilters({ ...filters, rating: v })} />
          </div>
        </FilterSection>

        {/* Creator Connections */}
        <div style={{ borderTop: '1px solid var(--line)', position: 'relative' }}>
          <div style={{
            position: 'absolute', inset: 0,
            background: 'radial-gradient(ellipse 60% 80% at 90% 0%, oklch(0.66 0.22 250 / 0.14), transparent 60%)',
            pointerEvents: 'none',
          }} />
          <div
            onClick={() => toggleSec('creator')}
            style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 20px', cursor: 'pointer', position: 'relative', userSelect: 'none' }}
          >
            <span style={{
              display: 'inline-flex', width: 18, height: 18, alignItems: 'center', justifyContent: 'center',
              transform: open.creator ? 'rotate(0deg)' : 'rotate(-90deg)', transition: 'transform 0.18s',
              color: 'var(--text-2)',
            }}>
              <Icons.chevDown size={13}/>
            </span>
            <Icons.zap size={14} style={{ color: 'var(--accent)' }}/>
            <div style={{ fontSize: 13, fontWeight: 600 }}>Creator Connections</div>
            <span className="card-eyebrow" style={{ marginLeft: 4 }}>Optional · overlaid from imported data</span>
            <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 10 }} onClick={(e) => e.stopPropagation()}>
              <span style={{ fontSize: 12, color: 'var(--text-2)' }}>Filter by CC</span>
              <Toggle on={filters.creator} onChange={(v) => { setFilters({ ...filters, creator: v }); if (v) setOpen(o => ({ ...o, creator: true })); }} />
            </div>
          </div>
          {open.creator && (
            <div style={{ position: 'relative' }}>
              <div style={{ padding: '4px 20px 12px', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16 }}>
                <Field label="Campaign start date">
                  <div style={{ display: 'flex', gap: 6 }}>
                    <input type="date" className="field-input" style={{ flex: 1 }}
                      value={filters.campaignStart[0]}
                      onChange={e => setFilters({ ...filters, campaignStart: [e.target.value, filters.campaignStart[1]] })} />
                    <input type="date" className="field-input" style={{ flex: 1 }}
                      value={filters.campaignStart[1]}
                      onChange={e => setFilters({ ...filters, campaignStart: [filters.campaignStart[0], e.target.value] })} />
                  </div>
                </Field>
                <Field label="Campaign end date">
                  <div style={{ display: 'flex', gap: 6 }}>
                    <input type="date" className="field-input" style={{ flex: 1 }}
                      value={filters.campaignEnd[0]}
                      onChange={e => setFilters({ ...filters, campaignEnd: [e.target.value, filters.campaignEnd[1]] })} />
                    <input type="date" className="field-input" style={{ flex: 1 }}
                      value={filters.campaignEnd[1]}
                      onChange={e => setFilters({ ...filters, campaignEnd: [filters.campaignEnd[0], e.target.value] })} />
                  </div>
                </Field>
              </div>
              <div style={{
                padding: '0 20px 18px', display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 16,
                opacity: filters.creator ? 1 : 0.45, pointerEvents: filters.creator ? 'auto' : 'none',
                transition: 'opacity 0.2s',
              }}>
                <Field label="Campaign percentage">
                  <NumPair values={filters.campaignPct} onChange={(v) => setFilters({ ...filters, campaignPct: v })} placeholderA="Min %" placeholderB="Max %" />
                </Field>
                <Field label="Campaign budget ($)">
                  <NumPair values={filters.campaignBudget} onChange={(v) => setFilters({ ...filters, campaignBudget: v })} />
                </Field>
                <Field label="Remaining budget ($)">
                  <NumPair values={filters.remainingBudget} onChange={(v) => setFilters({ ...filters, remainingBudget: v })} />
                </Field>
              </div>
            </div>
          )}
        </div>
      </div>

      {/* RESULTS HEADER */}
      <div style={{ display: 'flex', alignItems: 'center', marginTop: 24, marginBottom: 12, gap: 12 }}>
        <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600, letterSpacing: '-0.2px' }}>Results</h2>
        {results !== null && (
          <span style={{ fontSize: 12, color: 'var(--text-2)' }}>
            <span style={{ color: 'var(--text-0)', fontFamily: 'var(--font-mono)' }}>{sorted.length}</span>
            {totalMatches !== null && <> of <span style={{ fontFamily: 'var(--font-mono)' }}>{totalMatches.toLocaleString()}</span> Keepa matches</>}
          </span>
        )}

        <div style={{ marginLeft: 'auto', display: 'flex', gap: 10, alignItems: 'center' }}>
          {selected.size > 0 && (
            <>
              <span style={{ fontSize: 12, color: 'var(--accent)' }}>
                <span style={{ fontFamily: 'var(--font-mono)' }}>{selected.size}</span> selected
              </span>
              <div style={{ position: 'relative' }}>
                <button className="btn btn-primary btn-sm" onClick={() => setShowAddPicker(s => !s)}>
                  <Icons.plus size={13}/> Add to Leads
                </button>
                {showAddPicker && (
                  <div style={{
                    position: 'absolute', top: '110%', right: 0, zIndex: 10,
                    background: 'var(--bg-elev)', border: '1px solid var(--line-strong)',
                    borderRadius: 10, padding: 6, minWidth: 200,
                    boxShadow: '0 12px 40px -8px rgba(0,0,0,0.6)',
                  }}>
                    <div style={{ padding: '8px 10px', fontSize: 11, color: 'var(--text-3)', letterSpacing: 1, textTransform: 'uppercase', fontWeight: 600 }}>Send to list</div>
                    {leads.lists.map(l => (
                      <div key={l.id} className="list-pill" onClick={() => addSelectedToList(l.id)}>
                        <span className="dot-sq" style={{ background: l.color }}/>
                        <span className="name">{l.name}</span>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </>
          )}
          <button className="btn btn-sm" onClick={() => setShowColumns(true)}><Icons.columns size={13}/> Manage columns</button>
          <Segmented value={view} onChange={setView} options={[
            { value: 'table', label: 'Table', icon: Icons.list },
            { value: 'cards', label: 'Cards', icon: Icons.layout },
          ]} />
        </div>
      </div>

      <div className="card" style={{ overflow: 'hidden' }}>

        {/* ── SEARCHING ── */}
        {searching && (
          <div style={{ padding: '64px 32px', textAlign: 'center' }}>
            <div style={{
              width: 44, height: 44, borderRadius: 12, margin: '0 auto 16px',
              background: 'var(--accent-soft)', color: 'var(--accent)',
              display: 'grid', placeItems: 'center',
              animation: 'spin 1s linear infinite',
            }}>
              <Icons.bolt size={20}/>
            </div>
            <div style={{ fontWeight: 600, fontSize: 15 }}>Searching Keepa…</div>
            <div style={{ color: 'var(--text-3)', fontSize: 12, marginTop: 6 }}>Fetching product data and overlaying CC info</div>
          </div>
        )}

        {/* ── ERROR ── */}
        {!searching && searchError && (
          <div style={{ padding: 28, margin: 20, borderRadius: 10, background: 'color-mix(in srgb, var(--danger) 8%, var(--bg-1))', border: '1px solid var(--danger)' }}>
            <div style={{ fontWeight: 600, color: 'var(--danger)', marginBottom: 4 }}>Search failed</div>
            <div style={{ fontSize: 13, color: 'var(--text-2)' }}>{searchError}</div>
            <button className="btn btn-ghost" style={{ marginTop: 12 }} onClick={() => setSearchError(null)}>Dismiss</button>
          </div>
        )}

        {/* ── NOT SEARCHED YET ── */}
        {!searching && !searchError && results === null && (
          <div style={{ padding: '64px 32px', textAlign: 'center' }}>
            <div style={{
              width: 44, height: 44, borderRadius: 12, margin: '0 auto 16px',
              background: 'var(--bg-2)', color: 'var(--text-3)',
              display: 'grid', placeItems: 'center',
            }}>
              <Icons.search size={20}/>
            </div>
            <div style={{ fontWeight: 600, fontSize: 15, color: 'var(--text-1)' }}>Set filters and click Search</div>
            <div style={{ color: 'var(--text-3)', fontSize: 13, marginTop: 6, maxWidth: 400, margin: '8px auto 0' }}>
              Results come from Keepa's Product Finder. CC data from your imported spreadsheets will be overlaid automatically.
            </div>
            <button className="btn btn-primary" style={{ marginTop: 20 }} onClick={runKeepaSearch}>
              <Icons.search size={13}/> Run search
            </button>
          </div>
        )}

        {/* ── NO RESULTS ── */}
        {!searching && !searchError && results !== null && results.length === 0 && (
          <div style={{ padding: '48px 32px', textAlign: 'center' }}>
            <div style={{ fontWeight: 600, fontSize: 15, color: 'var(--text-1)' }}>No products found</div>
            <div style={{ color: 'var(--text-3)', fontSize: 13, marginTop: 6 }}>Try relaxing your filters and searching again.</div>
          </div>
        )}

        {/* ── TABLE ── */}
        {!searching && !searchError && sorted.length > 0 && view === 'table' && (
          <div className="scroll-x">
            <table className="data-table">
              <thead>
                <tr>
                  <th style={{ width: 36 }}><Checkbox checked={selected.size > 0 && selected.size === sorted.length} onChange={toggleAll}/></th>
                  {visibleCols.map(key => {
                    const std  = COLUMN_BY_KEY[key];
                    const cust = !std && customCols.find(c => c.key === key);
                    if (!std && !cust) return null;
                    const num = std?.num;
                    if (cust) {
                      return (
                        <th key={key} style={{ textAlign: 'left' }}>
                          <Icons.edit size={10} style={{ marginRight: 5, color: 'var(--accent)', verticalAlign: 'middle' }}/>
                          {cust.label}
                        </th>
                      );
                    }
                    return (
                      <th key={key} className="sortable" onClick={() => setSortKey(key)} style={{ textAlign: num ? 'right' : 'left' }}>
                        {std.label}
                        <span className="sort">{sort.key === key ? (sort.dir === 'asc' ? '↑' : '↓') : '↕'}</span>
                      </th>
                    );
                  })}
                  <th style={{ width: 90, textAlign: 'right' }}>Add</th>
                </tr>
              </thead>
              <tbody>
                {sorted.map((p, i) => (
                  <tr key={p.id} className={selected.has(p.id) ? 'selected' : ''} onClick={() => toggleSel(p.id)}>
                    <td><Checkbox checked={selected.has(p.id)} onChange={() => toggleSel(p.id)}/></td>
                    {visibleCols.map(key => {
                      if (isCustomKey(key)) {
                        const val = notes[p.id]?.[key] || '';
                        return (
                          <td key={key} onClick={(e) => e.stopPropagation()}>
                            <input
                              placeholder="Add note…"
                              value={val}
                              onChange={(e) => updateNote(p.id, key, e.target.value)}
                              style={{
                                width: 140, padding: '5px 8px',
                                background: 'transparent', border: '1px dashed transparent',
                                borderRadius: 6, color: 'var(--text-0)', font: 'inherit',
                                outline: 'none', transition: 'all 0.15s',
                              }}
                              onFocus={(e) => e.target.style.borderColor = 'var(--accent-soft)'}
                              onBlur={(e) => e.target.style.borderColor = 'transparent'}
                            />
                          </td>
                        );
                      }
                      const c = COLUMN_BY_KEY[key]; if (!c) return null;
                      return (
                        <td key={key} style={{ textAlign: c.num ? 'right' : 'left' }}>
                          {renderCellValue(key, p, i, fmtUSD, fmtInt, fmtBSR)}
                        </td>
                      );
                    })}
                    <td style={{ textAlign: 'right' }}>
                      <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }} onClick={(e) => e.stopPropagation()}>
                        <a
                          href={`https://www.amazon.com/dp/${p.asin}`}
                          target="_blank"
                          rel="noopener noreferrer"
                          className="icon-btn"
                          style={{ width: 28, height: 28, display: 'grid', placeItems: 'center' }}
                          title="View on Amazon"
                        >
                          <Icons.ext size={12}/>
                        </a>
                        <button className="icon-btn" style={{ width: 28, height: 28 }} title="Add to Leads"
                          onClick={() => { setSelected(new Set([p.id])); setShowAddPicker(true); }}>
                          <Icons.plus size={13}/>
                        </button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {/* ── CARDS ── */}
        {!searching && !searchError && sorted.length > 0 && view === 'cards' && (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 14, padding: 16 }}>
            {sorted.map((p, i) => (
              <div key={p.id} style={{
                background: 'var(--bg-2)', border: '1px solid var(--line)',
                borderRadius: 12, padding: 14, display: 'flex', gap: 12,
              }}>
                <ProductImage seed={i + 5} label={p.name}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="product-name" style={{ maxWidth: 'none', whiteSpace: 'normal' }}>{p.name}</div>
                  <div className="product-asin">{p.brand} · {p.asin}</div>
                  <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap' }}>
                    <span className={`badge ${p.roi >= 30 ? 'badge-success' : 'badge-warn'}`}>{p.roi}% ROI</span>
                    <span className="badge badge-neutral">{fmtUSD(p.price)}</span>
                    {p.hasCC && <span className="badge badge-accent">CC {p.cc}%</span>}
                  </div>
                  <div style={{ marginTop: 10 }}><Sparkline data={p.trend} w={220} h={32}/></div>
                </div>
              </div>
            ))}
          </div>
        )}

        {/* ── PAGINATION / footer ── */}
        {sorted.length > 0 && (
          <div className="pagination">
            <span>Showing {sorted.length} result{sorted.length !== 1 ? 's' : ''}</span>
            {totalMatches !== null && totalMatches > sorted.length && (
              <span style={{ fontSize: 12, color: 'var(--text-2)' }}>
                {(totalMatches - sorted.length).toLocaleString()} more available — refine filters or increase token budget
              </span>
            )}
          </div>
        )}
      </div>

      {showColumns && (
        <ManageColumnsDrawer
          order={order} setOrder={setOrder}
          hidden={hidden} setHidden={setHidden}
          customCols={customCols} setCustomCols={setCustomCols}
          defaultOrder={SEARCH_DEFAULT_ORDER}
          onClose={() => setShowColumns(false)}
        />
      )}
    </div>
  );
};

window.SearchPage = SearchPage;
