// Search row — the prototype's band of discrete controls, sitting directly under
// the top bar: date, guests, location, products, operator, keyword, Search, and
// the List/Grid/Map toggle on the same row (Agency Portal Prototype.dc.html,
// lines 57-101). Selected products render as removable chips underneath.
//
// This replaced the portal's single rounded search pill, and with it the omni
// dropdown and the AI natural-language search: the prototype's keyword box is a
// plain filter, and a free-text field that silently rewrote the other filters is
// exactly what the design drops. The operator filter is kept — partners work
// several operators and the prototype's fixture data had only one.

// A prototype-styled popover button. Reuses the portal's click-outside plumbing.
function SearchPop({ icon: Ico, label, active, open, onToggle, onClose, width, align, children }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open, onClose]);
  return (
    <div className="pp-sr-wrap" ref={ref}>
      <button type="button"
              className={"pp-sr-btn" + (active ? " is-active" : "") + (open ? " is-open" : "")}
              onClick={onToggle}>
        {Ico && <Ico size={14}/>}
        <span>{label}</span>
        <IconChevronDn size={11}/>
      </button>
      {open && (
        <div className={"pp-sr-pop" + (align === 'right' ? " is-right" : "")} style={width ? { width } : undefined}>
          {typeof children === 'function' ? children() : children}
        </div>
      )}
  </div>
  );
}

function SearchRow({ value, onChange, onSubmit, onClear, view, setView }) {
  const v = value;
  const set = (patch) => onChange({ ...v, ...patch });
  const [pop, setPop] = React.useState(null);   // 'date' | 'city' | 'products' | 'operator'
  const [typeQ, setTypeQ] = React.useState('');
  const [opQ, setOpQ] = React.useState('');
  const [cityQ, setCityQ] = React.useState('');
  const toggle = (name) => () => setPop(p => (p === name ? null : name));
  const close = () => setPop(null);

  const dateLabel = new Date(v.date + 'T00:00:00').toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' });

  // Three multi-selects with the same behaviour: search when the list is long,
  // checkboxes, and an empty selection meaning "all".
  const facets = {
    products: {
      key: 'productTypes', icon: IconFilter, noun: 'product', plural: 'products',
      options: PRODUCT_TYPES.map(p => ({ id: p.id, label: p.name })),
      query: typeQ, setQuery: setTypeQ,
    },
    city: {
      key: 'locations', icon: IconPin, noun: 'location', plural: 'locations',
      options: CITIES.map(c => ({ id: c, label: c })),
      query: cityQ, setQuery: setCityQ,
    },
    operator: {
      key: 'operatorIds', icon: IconTag, noun: 'operator', plural: 'operators',
      options: OPERATORS.slice().sort((a, b) => a.name.localeCompare(b.name, 'en-GB', { sensitivity: 'base' }))
        .map(o => ({ id: o.id, label: o.name, meta: o.venueCount })),
      query: opQ, setQuery: setOpQ,
    },
  };
  const labelFor = (f) => {
    const sel = v[f.key] || [];
    if (sel.length === 0) return 'All ' + f.plural;
    if (sel.length === 1) {
      const hit = f.options.find(o => o.id === sel[0]);
      return hit ? hit.label : sel[0];
    }
    return sel.length + ' ' + f.plural;
  };
  const toggleFacet = (f, id) => {
    const sel = v[f.key] || [];
    set({ [f.key]: sel.includes(id) ? sel.filter(x => x !== id) : [...sel, id] });
  };
  const renderFacet = (name) => {
    const f = facets[name];
    const q = f.query.trim().toLowerCase();
    const opts = f.options.filter(o => !q || o.label.toLowerCase().includes(q));
    const sel = v[f.key] || [];
    return (
      <React.Fragment>
        {f.options.length > 8 && (
          <div className="pp-sr-find">
            <IconSearch size={12}/>
            <input value={f.query} onChange={(e) => f.setQuery(e.target.value)}
                   placeholder={'Find one of ' + f.options.length + ' ' + f.plural}/>
          </div>
        )}
        {sel.length > 0 && (
          <button type="button" className="pp-sr-opt pp-sr-opt--reset"
                  onClick={() => set({ [f.key]: [] })}>All {f.plural}</button>
        )}
        {opts.map(o => {
          const on = sel.includes(o.id);
          return (
            <button type="button" key={o.id} className="pp-sr-opt pp-sr-opt--check"
                    onClick={() => toggleFacet(f, o.id)}>
              <span className={"pp-sr-box" + (on ? " is-on" : "")}>
                {on && <IconCheck size={11}/>}
              </span>
              <span className="pp-sr-opt-label">{o.label}</span>
              {o.meta != null && <span className="pp-sr-opt-meta">{o.meta}</span>}
            </button>
          );
        })}
        {opts.length === 0 && <div className="pp-sr-none">No {f.noun} matches “{f.query}”</div>}
      </React.Fragment>
    );
  };
  return (
    <form className="pp-searchrow" onSubmit={(e) => { e.preventDefault(); close(); onSubmit(); }}>
      <SearchPop icon={IconCal} label={dateLabel} active={pop === 'date'} open={pop === 'date'}
                 onToggle={toggle('date')} onClose={close} width={300}>
        {() => <Calendar value={v.date} minISO={ppLocalTodayISO()}
                         onChange={(iso) => { set({ date: iso }); close(); }}/>}
      </SearchPop>

      <div className="pp-sr-guests">
        <button type="button" aria-label="Fewer guests"
                onClick={() => set({ guests: Math.max(1, v.guests - 1) })}>−</button>
        <span>{v.guests} {v.guests === 1 ? 'guest' : 'guests'}</span>
        <button type="button" aria-label="More guests"
                onClick={() => set({ guests: Math.min(999, v.guests + 1) })}>+</button>
      </div>

      <SearchPop icon={facets.city.icon} label={labelFor(facets.city)}
                 active={(v.locations || []).length > 0}
                 open={pop === 'city'} onToggle={toggle('city')} onClose={close} width={260}>
        {() => renderFacet('city')}
      </SearchPop>

      <SearchPop icon={facets.products.icon} label={labelFor(facets.products)}
                 active={(v.productTypes || []).length > 0}
                 open={pop === 'products'} onToggle={toggle('products')} onClose={close} width={260}>
        {() => renderFacet('products')}
      </SearchPop>

      {/* Kept beyond the prototype: partners work several operator groups. */}
      <SearchPop icon={facets.operator.icon} label={labelFor(facets.operator)}
                 active={(v.operatorIds || []).length > 0}
                 open={pop === 'operator'} onToggle={toggle('operator')} onClose={close} width={260}>
        {() => renderFacet('operator')}
      </SearchPop>

      <div className="pp-sr-keyword">
        <IconSearch size={14}/>
        <input value={v.text} placeholder="Venue or keyword"
               onChange={(e) => set({ text: e.target.value })}/>
        {v.text ? (
          <button type="button" className="pp-sr-keyword-clear" aria-label="Clear keyword"
                  onClick={() => onClear({ ...v, text: '' })}>
            <IconClose size={11}/>
          </button>
        ) : null}
      </div>

      <button type="submit" className="pp-sr-go">
        <IconSearch size={13}/>
        <span>Search</span>
      </button>

      <span className="pp-sr-spacer"/>

      <div className="pp-sr-views" role="radiogroup" aria-label="View">
        {[['list', 'List', IconLayoutRows], ['grid', 'Grid', IconLayoutCols], ['map', 'Map', IconPin]].map(([id, label, Ico]) => (
          <button type="button" key={id} role="radio" aria-checked={view === id}
                  className={"pp-sr-view" + (view === id ? " is-active" : "")}
                  onClick={() => setView(id)}>
            <Ico size={13}/><span>{label}</span>
          </button>
        ))}
      </div>
    </form>
  );
}

// The band owns the draft, so typing a keyword or bumping guests re-renders these
// controls and nothing else. It used to live in ResultsScreen, which meant every
// keystroke reconciled 50 rows and ~490 chip buttons — 40-200ms of blocking work
// per character on a real catalogue.
//
// Nothing reaches the listing until Search is pressed (or a product chip is
// removed, which commits immediately — the prototype's chips are applied filters,
// not staged ones).
function SearchBand({ committed, onSearch, view, setView }) {
  const [draft, setDraft] = React.useState(committed);
  // Re-sync when the committed query changes from outside (a search, a reset).
  React.useEffect(() => { setDraft(committed); }, [committed]);
  return (
    <React.Fragment>
      <SearchRow value={draft} onChange={setDraft} onSubmit={() => onSearch(draft)}
                 onClear={(next) => { setDraft(next); onSearch(next); }}
                 view={view} setView={setView}/>
      <ProductChips value={draft} onChange={(next) => { setDraft(next); onSearch(next); }}/>
    </React.Fragment>
  );
}

// The prototype's chips strip, now covering all three multi-selects: every
// selected location, product and operator as a removable pill (BOO-644 QA).
function ProductChips({ value, onChange }) {
  const groups = [
    { key: 'locations', label: 'Locations', name: (id) => id },
    { key: 'productTypes', label: 'Products', name: (id) => (productById(id) || { name: id }).name },
    { key: 'operatorIds', label: 'Operators', name: (id) => ((operatorById(id) || {}).name || id) },
  ].filter(g => (value[g.key] || []).length > 0);
  if (groups.length === 0) return null;
  const drop = (key, id) => onChange({ ...value, [key]: (value[key] || []).filter(x => x !== id) });
  return (
    <div className="pp-sr-chips">
      {groups.map(g => (
        <React.Fragment key={g.key}>
          <span className="pp-sr-chips-label">{g.label}</span>
          {(value[g.key] || []).map(id => (
            <span className="pp-sr-chip" key={g.key + id}>
              {g.name(id)}
              <button type="button" aria-label={'Remove ' + g.name(id)} onClick={() => drop(g.key, id)}>
                <IconClose size={9}/>
              </button>
            </span>
          ))}
        </React.Fragment>
      ))}
      <button type="button" className="pp-sr-chips-clear"
              onClick={() => onChange({ ...value, locations: [], productTypes: [], operatorIds: [] })}>
        Clear filters
      </button>
    </div>
  );
}

function GuestStepper({ value, onChange, min = 1, max = 999 }) {
  // Local text state so partial typing (e.g. while typing "33", you don't see 3
  // then 33 trip back to default) feels natural. Commit on blur or Enter.
  const [text, setText] = React.useState(String(value));
  React.useEffect(() => { setText(String(value)); }, [value]);

  const commit = (raw) => {
    const n = parseInt(String(raw).replace(/[^0-9]/g, ''), 10);
    if (isNaN(n)) { setText(String(value)); return; }
    const clamped = Math.max(min, Math.min(max, n));
    onChange(clamped);
    setText(String(clamped));
  };

  const bump = (delta) => {
    const n = parseInt(text, 10);
    const base = isNaN(n) ? value : n;
    onChange(Math.max(min, Math.min(max, base + delta)));
  };

  return (
    <div className="pp-guest-stepper">
      <div className="pp-guest-stepper-label">Number of guests</div>
      <div className="pp-guest-stepper-row">
        <button type="button" className="pp-guest-stepper-btn"
                aria-label="Decrease"
                disabled={value <= min}
                onClick={() => bump(-1)}>
          <span>−</span>
        </button>
        <input className="pp-guest-stepper-input"
               inputMode="numeric"
               pattern="[0-9]*"
               value={text}
               aria-label="Number of guests"
               onChange={(e) => setText(e.target.value.replace(/[^0-9]/g, ''))}
               onBlur={(e) => commit(e.target.value)}
               onKeyDown={(e) => {
                 if (e.key === 'Enter') { e.preventDefault(); commit(e.currentTarget.value); e.currentTarget.blur(); }
                 if (e.key === 'ArrowUp')   { e.preventDefault(); bump(1); }
                 if (e.key === 'ArrowDown') { e.preventDefault(); bump(-1); }
               }}/>
        <button type="button" className="pp-guest-stepper-btn"
                aria-label="Increase"
                disabled={value >= max}
                onClick={() => bump(1)}>
          <span>+</span>
        </button>
      </div>
      <div className="pp-guest-stepper-hint">{value === 1 ? '1 guest' : value + ' guests'}</div>
    </div>
  );
}


// Month-grid calendar with prev/next nav. ISO date string in/out.
function Calendar({ value, onChange, minISO }) {
  const valDate = value ? new Date(value + 'T00:00:00') : new Date();
  const [cursor, setCursor] = React.useState(() => {
    const d = new Date(valDate); d.setDate(1); return d;
  });

  const year = cursor.getFullYear();
  const month = cursor.getMonth();
  const monthLabel = cursor.toLocaleDateString('en-GB', { month: 'long', year: 'numeric' });

  // Build the day grid — Mon-first.
  const firstOfMonth = new Date(year, month, 1);
  const firstWeekday = (firstOfMonth.getDay() + 6) % 7; // Mon=0..Sun=6
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const daysInPrevMonth = new Date(year, month, 0).getDate();

  const cells = [];
  // Leading days from prev month
  for (let i = firstWeekday - 1; i >= 0; i--) {
    const day = daysInPrevMonth - i;
    const d = new Date(year, month - 1, day);
    cells.push({ d, inMonth: false });
  }
  // This month
  for (let day = 1; day <= daysInMonth; day++) {
    cells.push({ d: new Date(year, month, day), inMonth: true });
  }
  // Trailing days to fill 6 weeks
  while (cells.length < 42) {
    const last = cells[cells.length - 1].d;
    const d = new Date(last); d.setDate(last.getDate() + 1);
    cells.push({ d, inMonth: d.getMonth() === month });
  }

  const today = new Date(); today.setHours(0,0,0,0);
  const isoOf = (d) => {
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2,'0');
    const day = String(d.getDate()).padStart(2,'0');
    return y + '-' + m + '-' + day;
  };
  const minDate = minISO ? new Date(minISO + 'T00:00:00') : null;

  const stepMonth = (delta) => {
    const c = new Date(cursor); c.setMonth(cursor.getMonth() + delta); setCursor(c);
  };
  const goToToday = () => {
    const c = new Date(); c.setDate(1); setCursor(c);
    onChange(isoOf(today));
  };

  return (
    <div className="pp-cal">
      <div className="pp-cal-head">
        <button type="button" className="pp-cal-nav" onClick={() => stepMonth(-1)} aria-label="Previous month"
                disabled={minDate && new Date(year, month, 0) < minDate}>
          <IconArrowL size={14}/>
        </button>
        <div className="pp-cal-month">{monthLabel}</div>
        <button type="button" className="pp-cal-nav" onClick={() => stepMonth(1)} aria-label="Next month">
          <IconArrowR size={14}/>
        </button>
      </div>
      <div className="pp-cal-grid pp-cal-grid--dow">
        {['Mo','Tu','We','Th','Fr','Sa','Su'].map((d, i) => (
          <div key={i} className="pp-cal-doh">{d}</div>
        ))}
      </div>
      <div className="pp-cal-grid">
        {cells.map((c, i) => {
          const iso = isoOf(c.d);
          const isSelected = iso === value;
          const isToday = c.d.getTime() === today.getTime();
          const isDisabled = minDate && c.d < minDate && !isSelected;
          return (
            <button key={i}
                    type="button"
                    disabled={isDisabled}
                    className={"pp-cal-cell"
                      + (c.inMonth ? '' : ' is-muted')
                      + (isSelected ? ' is-active' : '')
                      + (isToday ? ' is-today' : '')
                      + (isDisabled ? ' is-disabled' : '')}
                    onClick={() => onChange(iso)}>
              {c.d.getDate()}
            </button>
          );
        })}
      </div>
      <button type="button" className="pp-cal-today" onClick={goToToday}>Jump to today</button>
    </div>
  );
}

Object.assign(window, { SearchBand, SearchRow, ProductChips, SearchPop, GuestStepper, Calendar });
