// itrymo — Custom search input + analytics logger
// Free-text search: fuzzy-matches existing tags/places AND logs every query
// so itrymo can mine what users want that doesn't exist yet.

const ANALYTICS_KEY = 'itrymo_search_log';

// Natural-language → existing-tag synonyms. Lets "ramen", "sushi", "vegan",
// "rooftop"… resolve to the tags/places we actually have. Keys are matched
// as whole words or substrings of the query.
const SEARCH_SYNONYMS = {
  ramen: ['Noodles & Soups'], noodle: ['Noodles & Soups'], noodles: ['Noodles & Soups'],
  udon: ['Noodles & Soups'], soba: ['Noodles & Soups'], soup: ['Noodles & Soups'],
  pho: ['Noodles & Soups'], sushi: ['Fish'], sashimi: ['Fish'], seafood: ['Fish'], fish: ['Fish'],
  bbq: ['Beef'], yakiniku: ['Beef'], steak: ['Beef'], beef: ['Beef'],
  burger: ['Fast Food & Burgers'], burgers: ['Fast Food & Burgers'], fastfood: ['Fast Food & Burgers'],
  pizza: ['Pizza'], chicken: ['Chicken'], fried: ['Chicken', 'Fast Food & Burgers'],
  coffee: ['Coffee'], cafe: ['Coffee'], 'café': ['Coffee'], espresso: ['Coffee'], latte: ['Coffee'],
  tea: ['Milk Tea'], boba: ['Milk Tea'], bubble: ['Milk Tea'], matcha: ['Milk Tea'],
  dessert: ['Desserts'], desserts: ['Desserts'], sweet: ['Desserts'], cake: ['Desserts'], icecream: ['Desserts'],
  bread: ['Bread'], bakery: ['Bread'], pastry: ['Bread'],
  vegan: ['Vegetarian'], vegetarian: ['Vegetarian'], veggie: ['Vegetarian'], plant: ['Vegetarian'],
  halal: ['Halal'], pork: ['Pork Free'], organic: ['Organic'],
  spicy: ['Spicy'], hot: ['Spicy'], street: ['Street Food'], cheap: ['Street Food'], budget: ['Street Food'],
  hidden: ['Local Hidden Gems'], gem: ['Local Hidden Gems'], local: ['Local Hidden Gems'],
  themed: ['Themed Cafés & Dining'], theme: ['Themed Cafés & Dining'],
  japanese: ['Japanese'], korean: ['Korean'], chinese: ['Chinese'], italian: ['Italian'],
  thai: ['Thai'], indian: ['Indian'], mexican: ['Mexican'], vietnamese: ['Vietnamese'], american: ['American'],
  // do
  museum: ['Museums & Galleries'], museums: ['Museums & Galleries'], art: ['Museums & Galleries'], gallery: ['Museums & Galleries'],
  history: ['Historical Places'], historical: ['Historical Places'], temple: ['Historical Places'], shrine: ['Historical Places'], castle: ['Castles & Fortresses'],
  nightlife: ['Bars & Nightlife'], bar: ['Bars & Nightlife'], bars: ['Bars & Nightlife'], club: ['Bars & Nightlife'], drinks: ['Bars & Nightlife'],
  shopping: ['Shopping Malls'], shop: ['Shopping Malls'], mall: ['Shopping Malls'],
  park: ['Parks & Gardens'], parks: ['Parks & Gardens'], garden: ['Parks & Gardens'], nature: ['Landscapes & Nature'],
  view: ['Scenic Views & Rooftops'], views: ['Scenic Views & Rooftops'], rooftop: ['Scenic Views & Rooftops'], tower: ['Towers & Overlooks'],
  beach: ['Beaches'], beaches: ['Beaches'], hike: ['Hiking & Mountaineering'], hiking: ['Hiking & Mountaineering'], mountain: ['Hiking & Mountaineering'],
  surf: ['Surfing & Wakeboarding'], dive: ['Diving & Snorkeling'], diving: ['Diving & Snorkeling'], snorkel: ['Diving & Snorkeling'],
  kayak: ['Kayaking & Rafting'], zipline: ['Ziplining'], skydive: ['Skydiving'], wellness: ['Traditional Wellness'], spa: ['Traditional Wellness'],
  workshop: ['Cultural Workshops'], solo: ['Solo Friendly'], pet: ['Pet Friendly'], festival: ['Local Festivals'], zoo: ['Zoos & Aquariums'], aquarium: ['Zoos & Aquariums'],
  // stay
  hotel: ['Boutique Hotel'], boutique: ['Boutique Hotel'], hostel: ['Hostel'], resort: ['Resort'],
  ryokan: ['Ryokan'], apartment: ['Apartment'], airbnb: ['Apartment'], guesthouse: ['Guesthouse'], luxury: ['Resort'],
};

// Expand a query into a set of canonical tag terms (lowercased) via synonyms.
function expandQuery(lower) {
  const terms = new Set();
  const words = lower.split(/[\s,]+/).filter(Boolean);
  const check = (w) => {
    if (w.length < 2) return;
    Object.keys(SEARCH_SYNONYMS).forEach((k) => {
      if (w === k || w.includes(k) || (w.length >= 3 && k.includes(w))) {
        SEARCH_SYNONYMS[k].forEach((t) => terms.add(t.toLowerCase()));
      }
    });
  };
  check(lower);
  words.forEach(check);
  return [...terms];
}

function logSearch(query, matched, bucket) {
  if (!query || query.trim().length < 2) return;
  try {
    const log = JSON.parse(localStorage.getItem(ANALYTICS_KEY) || '[]');
    log.push({
      q: query.trim(),
      matched: !!matched,
      bucket,
      ts: Date.now(),
    });
    // Keep last 200 to bound size
    if (log.length > 200) log.splice(0, log.length - 200);
    localStorage.setItem(ANALYTICS_KEY, JSON.stringify(log));
    window.dispatchEvent(new Event('itrymo:analytics:update'));
  } catch (e) {}
}

function useAnalyticsLog() {
  const [log, setLog] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem(ANALYTICS_KEY) || '[]'); } catch { return []; }
  });
  React.useEffect(() => {
    const handler = () => {
      try { setLog(JSON.parse(localStorage.getItem(ANALYTICS_KEY) || '[]')); } catch {}
    };
    window.addEventListener('itrymo:analytics:update', handler);
    return () => window.removeEventListener('itrymo:analytics:update', handler);
  }, []);
  return log;
}

// Debounced search submit logger — fires when user stops typing
function useDebouncedLog(query, matched, bucket, delay = 800) {
  React.useEffect(() => {
    if (!query) return;
    const t = setTimeout(() => logSearch(query, matched, bucket), delay);
    return () => clearTimeout(t);
  }, [query, matched, bucket, delay]);
}

// ─────────────────────────────────────────────────────────────
// CustomSearch — free-text input above tag inputs
// - Live results: matching tags + matching places (by name/area)
// - "Suggest a tag" CTA when no matches
// - Logs every settled query (debounced)
// ─────────────────────────────────────────────────────────────
function CustomSearch({ bucket, groups, tags, onAddTag, places, onPickPlace, searchQuery, onSearchChange }) {
  const [q, setQ] = React.useState(searchQuery || '');
  const [focused, setFocused] = React.useState(false);

  // Sync internal q with external searchQuery when cleared
  React.useEffect(() => { if (!searchQuery) setQ(''); }, [searchQuery]);
  const [suggested, setSuggested] = React.useState([]); // user-suggested tags this session
  const wrapRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const tagCounts = React.useMemo(() => getTagCounts(places), [places]);

  React.useEffect(() => {
    if (!focused) return;
    const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setFocused(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [focused]);

  // Match against existing tags across all groups + place names + areas + tags,
  // expanded with synonyms so natural queries ("ramen", "rooftop") resolve.
  const lower = q.trim().toLowerCase();
  const synTerms = lower ? expandQuery(lower) : [];

  // Tag suggestions: direct substring matches + synonym-mapped tags in this bucket.
  const directTags = lower ? suggestTags(q, groups, tagCounts, tags, 8) : [];
  const allBucketTags = React.useMemo(
    () => groups.flatMap((g) => g.tags.map((t) => ({ tag: t, group: g.label, groupId: g.id }))),
    [groups]
  );
  const synTags = synTerms.length
    ? allBucketTags
        .filter((t) => synTerms.includes(t.tag.toLowerCase()) && !directTags.some((d) => d.tag === t.tag))
        .map((t) => ({ tag: t.tag, group: t.group, groupId: t.groupId, count: tagCounts.get(t.tag) || 0, active: tags.includes(t.tag) }))
    : [];
  const tagMatches = [...directTags, ...synTags].slice(0, 8);

  // Place matches: scope to the current bucket; match name, area, or any tag
  // (literal or via synonyms).
  const placeMatches = lower
    ? places.filter((p) => {
        if (bucket && bucket !== 'all' && p.bucket !== bucket) return false;
        const hay = (p.name + ' ' + p.area + ' ' + (p.tags || []).join(' ')).toLowerCase();
        if (hay.includes(lower)) return true;
        return synTerms.some((t) => (p.tags || []).some((tag) => tag.toLowerCase().includes(t)));
      }).slice(0, 5)
    : [];
  const anyMatch = tagMatches.length > 0 || placeMatches.length > 0;

  useDebouncedLog(q, anyMatch, bucket);

  const handleSuggest = () => {
    if (!q.trim()) return;
    setSuggested((s) => s.includes(q.trim()) ? s : [...s, q.trim()]);
    logSearch(q, false, bucket); // force-log suggestion
    setQ('');
    setFocused(false);
  };

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <div onClick={() => { setFocused(true); inputRef.current && inputRef.current.focus(); }} style={{
        height: 44, borderRadius: 12,
        background: '#fff',
        border: `1px solid ${focused ? T.selBg : T.border}`,
        padding: '0 12px', display: 'flex', alignItems: 'center', gap: 8,
        boxShadow: focused ? `0 0 0 3px ${T.accentBg}` : 'none',
        transition: 'border-color .15s, box-shadow .15s',
        cursor: 'text',
      }}>
        <Icon name="search" size={14} color={focused ? T.ink : T.muted} />
        <input
          ref={inputRef}
          value={q}
          onChange={(e) => { setQ(e.target.value); onSearchChange && onSearchChange(e.target.value); }}
          onFocus={() => setFocused(true)}
          placeholder="Try 'late night ramen', 'kid friendly café', 'cheap eats'…"
          style={{
            all: 'unset', flex: 1, fontSize: 13, color: T.ink, fontFamily: T.fontUI,
          }}
        />
        {q && (
          <button onClick={(e) => { e.stopPropagation(); setQ(''); onSearchChange && onSearchChange(''); }} style={{
            all: 'unset', cursor: 'pointer', color: T.muted, padding: 4,
          }}>
            <Icon name="close" size={12} />
          </button>
        )}
      </div>

      {/* Suggested-this-session pills (not real filters; chips showing what was suggested) */}
      {suggested.length > 0 && (
        <div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {suggested.map((s) => (
            <span key={s} style={{
              fontSize: 11, padding: '3px 8px', borderRadius: 999,
              background: T.accentBg, color: T.accent, border: `1px dashed ${T.accentBd}`,
              fontWeight: 500,
            }}>
              <Icon name="check" size={10} style={{ verticalAlign: -1, marginRight: 4 }}/>
              Suggested: {s}
            </span>
          ))}
        </div>
      )}

      {focused && q && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, zIndex: 40,
          background: '#fff', borderRadius: 12,
          border: `1px solid ${T.border}`, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
          maxHeight: 320, overflowY: 'auto', padding: 6,
        }}>
          {tagMatches.length > 0 && (
            <>
              <div style={{
                padding: '6px 10px 4px', fontSize: 10, color: T.muted,
                letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: 700,
              }}>Existing tags</div>
              {tagMatches.map((s) => (
                <TagSuggestion key={s.tag} s={{ ...s, active: tags.includes(s.tag) }}
                               query={q} onToggle={onAddTag} />
              ))}
            </>
          )}
          {placeMatches.length > 0 && (
            <>
              <div style={{
                padding: '10px 10px 4px', fontSize: 10, color: T.muted,
                letterSpacing: 0.6, textTransform: 'uppercase', fontWeight: 700,
              }}>Places</div>
              {placeMatches.map((p) => (
                <button key={p.id} onClick={() => onPickPlace && onPickPlace(p)} style={{
                  all: 'unset', cursor: 'pointer', display: 'flex',
                  width: '100%', boxSizing: 'border-box',
                  padding: '8px 10px', borderRadius: 8, gap: 10, alignItems: 'center',
                }}>
                  <div style={{
                    width: 32, height: 32, borderRadius: 8,
                    background: `repeating-linear-gradient(135deg, ${p.swatch} 0 8px, ${p.swatch}b3 8px 16px)`,
                    flexShrink: 0,
                  }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      <Highlight text={p.name} query={q} />
                    </div>
                    <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>
                      <Highlight text={p.area} query={q} /> · ★ {p.rating.toFixed(1)}
                    </div>
                  </div>
                </button>
              ))}
            </>
          )}
          {!anyMatch && (
            <div style={{ padding: '12px 10px' }}>
              <div style={{ fontSize: 12, color: T.muted, marginBottom: 8 }}>
                No tags or places match "<strong style={{ color: T.ink }}>{q}</strong>".
              </div>
              <button onClick={handleSuggest} style={{
                all: 'unset', cursor: 'pointer',
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '8px 12px', borderRadius: 999,
                background: T.selBg, color: T.selFg,
                fontSize: 12, fontWeight: 600,
              }}>
                <Icon name="plus" size={12} /> Suggest "{q}" as a tag
              </button>
              <div style={{ fontSize: 11, color: T.muted, marginTop: 8 }}>
                We'll review and add it if enough people ask.
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AnalyticsPanel — floating widget that shows recent search log
// Toggled by a small "Analytics" button. Only visible in v3.
// ─────────────────────────────────────────────────────────────
function AnalyticsPanel() {
  const [open, setOpen] = React.useState(false);
  const log = useAnalyticsLog();

  // Aggregate
  const counts = new Map();
  log.forEach((e) => {
    const k = e.q.toLowerCase();
    if (!counts.has(k)) counts.set(k, { q: e.q, count: 0, matched: 0, missed: 0 });
    const c = counts.get(k);
    c.count += 1;
    if (e.matched) c.matched += 1; else c.missed += 1;
  });
  const top = Array.from(counts.values()).sort((a, b) => b.count - a.count).slice(0, 20);
  const totalQueries = log.length;
  const missRate = log.length ? Math.round(log.filter((e) => !e.matched).length / log.length * 100) : 0;

  return (
    <>
      <button onClick={() => setOpen(true)} style={{
        position: 'fixed', bottom: 20, right: 20, zIndex: 100,
        height: 38, padding: '0 14px', borderRadius: 999,
        background: T.selBg, color: T.selFg, border: 'none',
        display: 'inline-flex', alignItems: 'center', gap: 6,
        fontSize: 12, fontWeight: 600, cursor: 'pointer',
        boxShadow: '0 8px 24px rgba(0,0,0,0.20)',
        fontFamily: T.fontUI,
      }}>
        <Icon name="sparkle" size={12} />
        Search analytics · {totalQueries}
      </button>

      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{
            position: 'fixed', inset: 0, zIndex: 110, background: 'rgba(0,0,0,0.30)',
          }} />
          <div style={{
            position: 'fixed', right: 20, bottom: 70, zIndex: 120,
            width: 360, maxHeight: '70vh', background: '#fff', borderRadius: 16,
            border: `1px solid ${T.border}`, boxShadow: '0 24px 64px rgba(0,0,0,0.25)',
            display: 'flex', flexDirection: 'column', fontFamily: T.fontUI,
          }}>
            <div style={{
              padding: '14px 18px', borderBottom: `1px solid ${T.borderSoft}`,
              display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            }}>
              <div>
                <div style={{ fontSize: 14, fontWeight: 700 }}>What users are typing</div>
                <div style={{ fontSize: 11, color: T.muted, marginTop: 2 }}>
                  {totalQueries} {totalQueries === 1 ? 'query' : 'queries'} logged · {missRate}% miss rate
                </div>
              </div>
              <button onClick={() => setOpen(false)} style={{
                all: 'unset', cursor: 'pointer',
                width: 28, height: 28, borderRadius: 14, background: '#F4EFE6',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              }}>
                <Icon name="close" size={12} />
              </button>
            </div>

            <div style={{ overflowY: 'auto', flex: 1, padding: '8px 0' }}>
              {top.length === 0 ? (
                <div style={{ padding: '40px 20px', textAlign: 'center', color: T.muted, fontSize: 12 }}>
                  Nothing logged yet. Try searching above.
                </div>
              ) : (
                top.map((row) => (
                  <div key={row.q} style={{
                    padding: '8px 18px', display: 'flex', alignItems: 'center', gap: 10,
                  }}>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 500, color: T.ink,
                                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {row.q}
                      </div>
                      <div style={{ fontSize: 11, color: T.muted, marginTop: 1 }}>
                        {row.count}× · {row.matched} matched · {row.missed} missed
                      </div>
                    </div>
                    {row.missed > 0 && row.matched === 0 && (
                      <span style={{
                        fontSize: 10, padding: '2px 8px', borderRadius: 999,
                        background: T.accentBg, color: T.accent, fontWeight: 600,
                        border: `1px solid ${T.accentBd}`,
                      }}>Gap</span>
                    )}
                  </div>
                ))
              )}
            </div>

            <div style={{
              padding: '10px 18px', borderTop: `1px solid ${T.borderSoft}`,
              display: 'flex', justifyContent: 'space-between',
            }}>
              <button onClick={() => {
                localStorage.removeItem(ANALYTICS_KEY);
                window.dispatchEvent(new Event('itrymo:analytics:update'));
              }} style={{
                all: 'unset', cursor: 'pointer', fontSize: 12, color: T.muted,
              }}>Clear log</button>
              <button onClick={() => {
                const csv = ['query,count,matched,missed', ...top.map((r) => `"${r.q}",${r.count},${r.matched},${r.missed}`)].join('\n');
                const blob = new Blob([csv], { type: 'text/csv' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url; a.download = 'itrymo-searches.csv'; a.click();
                URL.revokeObjectURL(url);
              }} style={{
                all: 'unset', cursor: 'pointer', fontSize: 12, color: T.accent, fontWeight: 600,
              }}>Export CSV</button>
            </div>
          </div>
        </>
      )}
    </>
  );
}

Object.assign(window, {
  CustomSearch, AnalyticsPanel, logSearch, useAnalyticsLog,
});
