/* Catalog component — M.A. Apartments
   Bar → Rail transformation with smooth FLIP-style motion
*/

const { useState, useEffect, useMemo, useRef, useLayoutEffect } = React;

const fmtPrice = (n) => n.toLocaleString('ru-RU').replace(/,/g, ' ') + ' ₸';

function Chip({ active, onClick, children, vertical }) {
  return (
    <button className={"f-chip" + (active ? " is-active" : "") + (vertical ? " is-v" : "")} onClick={onClick}>
      {children}
    </button>
  );
}

function RangeField({ label, suffix, min, max, value, onChange, step = 1, compact }) {
  return (
    <div className={"f-range" + (compact ? " is-compact" : "")}>
      <div className="f-range-head">
        <span className="f-range-label">{label}</span>
        <span className="f-range-val">
          {value[0].toLocaleString('ru-RU')}—{value[1].toLocaleString('ru-RU')} {suffix}
        </span>
      </div>
      <div className="f-range-track">
        <div className="f-range-fill"
          style={{ left: ((value[0]-min)/(max-min))*100+'%', right: 100-((value[1]-min)/(max-min))*100+'%' }} />
        <input type="range" min={min} max={max} step={step} value={value[0]}
          onChange={e => onChange([Math.min(+e.target.value, value[1]-step), value[1]])} />
        <input type="range" min={min} max={max} step={step} value={value[1]}
          onChange={e => onChange([value[0], Math.max(+e.target.value, value[0]+step)])} />
      </div>
    </div>
  );
}

function ApartmentCard({ apt, onOpen }) {
  return (
    <article className="apt-card" onClick={() => onOpen(apt)} data-id={apt.id}>
      <div className="apt-photo">
        <div className="apt-photo-frame">
          <img src={apt.cover} alt={apt.title} width={apt.w} height={apt.h} loading="lazy" />
          <div className="apt-photo-tone" />
          {apt.images && apt.images.length > 1 && (
            <span className="apt-photo-count">
              <svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.4">
                <rect x="2" y="3" width="9" height="9" rx="1" />
                <rect x="5" y="6" width="9" height="9" rx="1" />
              </svg>
              {apt.images.length}
            </span>
          )}
        </div>
        <span className="apt-pricetag">
          <span className="num">{fmtPrice(apt.price)}</span>
          <span className="per">/ ночь</span>
        </span>
      </div>
      <div className="apt-meta">
        <h3 className="apt-title">{apt.title}</h3>
        <div className="apt-loc">
          <span className="apt-bldg">{apt.building}</span>
          <span className="apt-dot">·</span>
          <span className="apt-addr">{apt.address}</span>
        </div>
        <div className="apt-stats">
          <span className="apt-stat">до {apt.guests} гостей</span>
          <span className="apt-stat">{apt.type}</span>
          <span className="apt-stat">{apt.area} м²</span>
        </div>
      </div>
    </article>
  );
}

function Catalog({ onOpen }) {
  const [data, setData] = useState([]);
  const [type, setType] = useState('all');
  const [building, setBuilding] = useState('all');
  const [district, setDistrict] = useState('all');
  const [guests, setGuests] = useState(0);
  const [price, setPrice] = useState([10000, 25000]);
  const [area, setArea] = useState([28, 75]);
  const [sort, setSort] = useState('featured');
  const [heroSearch, setHeroSearch] = useState(null);

  // mode: 'bar' (initial, in flow) or 'rail' (sticky vertical)
  const [mode, setMode] = useState('bar');
  const sentinelRef = useRef(null);

  useEffect(() => {
    fetch('data/apartments.json').then(r => r.json()).then(setData);
  }, []);

  // Listen for the Hero pill submission and translate it into filter state.
  useEffect(() => {
    const onHeroSearch = (e) => {
      const f = e.detail || {};
      if (f.district !== undefined) setDistrict(f.district || 'all');
      if (typeof f.guests === 'number') setGuests(f.guests);
      // Reset other dimensions so the user sees what their search produced.
      setType('all');
      setBuilding('all');
      setHeroSearch(f);
    };
    window.addEventListener('hero-search', onHeroSearch);
    return () => window.removeEventListener('hero-search', onHeroSearch);
  }, []);

  // Switch mode by scroll position relative to sentinel.
  // Hysteresis prevents flicker around the threshold.
  useEffect(() => {
    let currentMode = 'bar';
    const onScroll = () => {
      const el = sentinelRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      // Rail when sentinel is well above viewport top.
      // Different thresholds for entering and leaving = hysteresis.
      const next = currentMode === 'bar'
        ? (r.top < -120 ? 'rail' : 'bar')
        : (r.top > -40 ? 'bar' : 'rail');
      if (next !== currentMode) {
        currentMode = next;
        setMode(next);
      }
    };
    onScroll();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', onScroll);
    };
  }, []);

  const buildings = useMemo(() => {
    const set = new Set(data.map(d => d.building));
    return ['all', ...Array.from(set)];
  }, [data]);

  const filtered = useMemo(() => {
    let r = data.filter(d =>
      (type === 'all' || d.type === type) &&
      (building === 'all' || d.building === building) &&
      (district === 'all' || d.district === district) &&
      (guests === 0 || d.guests >= guests) &&
      d.price >= price[0] && d.price <= price[1] &&
      d.area >= area[0] && d.area <= area[1]
    );
    if (sort === 'price-asc') r = [...r].sort((a,b) => a.price - b.price);
    else if (sort === 'price-desc') r = [...r].sort((a,b) => b.price - a.price);
    else if (sort === 'area-desc') r = [...r].sort((a,b) => b.area - a.area);
    return r;
  }, [data, type, building, district, guests, price, area, sort]);

  // FLIP for cards
  const gridRef = useRef(null);
  const prevRects = useRef(new Map());
  useLayoutEffect(() => {
    const grid = gridRef.current;
    if (!grid) return;
    const cards = grid.querySelectorAll('.apt-card');
    cards.forEach(c => {
      const id = c.dataset.id;
      const newRect = c.getBoundingClientRect();
      const prev = prevRects.current.get(id);
      if (prev) {
        const dx = prev.left - newRect.left;
        const dy = prev.top - newRect.top;
        if (dx || dy) {
          c.style.transform = `translate(${dx}px, ${dy}px)`;
          c.style.transition = 'none';
          requestAnimationFrame(() => {
            c.style.transition = 'transform 520ms cubic-bezier(0.22, 1, 0.36, 1)';
            c.style.transform = '';
          });
        }
      }
      prevRects.current.set(id, newRect);
    });

    // Masonry: compute row spans based on each card's natural height.
    // Cards use margin-bottom for vertical spacing, so we don't fight grid row-gap.
    const ROW = 8; // grid-auto-rows in px
    const layout = () => {
      cards.forEach(c => {
        // Temporarily clear so height reflects content
        c.style.gridRowEnd = '';
        const h = c.getBoundingClientRect().height;
        if (!h) return;
        // +2 rows safety so cards never visually overlap
        const span = Math.ceil(h / ROW) + 3;
        c.style.gridRowEnd = 'span ' + span;
      });
    };
    layout();
    // Re-run when any card's box changes size (image load, font reflow, resize)
    const ro = new ResizeObserver(() => {
      // batch via rAF to avoid thrash
      cancelAnimationFrame(layout._raf);
      layout._raf = requestAnimationFrame(layout);
    });
    cards.forEach(c => ro.observe(c));
    const onR = () => layout();
    window.addEventListener('resize', onR);
    return () => {
      ro.disconnect();
      window.removeEventListener('resize', onR);
    };
  }, [filtered]);

  const types = ['all','Студия','1-комн','2-комн','3-комн'];
  const guestOpts = [0, 2, 4, 6];

  const reset = () => {
    setType('all'); setBuilding('all'); setDistrict('all'); setGuests(0);
    setPrice([10000, 25000]); setArea([28, 75]); setSort('featured');
    setHeroSearch(null);
  };

  // count active filters for rail summary
  const activeCount =
    (type !== 'all' ? 1 : 0) +
    (building !== 'all' ? 1 : 0) +
    (guests !== 0 ? 1 : 0) +
    ((price[0] !== 10000 || price[1] !== 25000) ? 1 : 0) +
    ((area[0] !== 28 || area[1] !== 75) ? 1 : 0);

  return (
    <section className={"catalog catalog--" + mode} id="catalog" data-screen-label="Каталог">
      <div className="catalog-head">
        <div className="catalog-eyebrow">Каталог · {data.length} объектов</div>
        <h2 className="catalog-h">
          Выберите квартиру <em>в&nbsp;Алматы</em>
        </h2>
      </div>

      <div className="catalog-body">
        {/* sentinel: when this scrolls above viewport, switch to rail */}
        <div ref={sentinelRef} className="catalog-sentinel" aria-hidden="true" />

        {/* Filterbar — single morphing element */}
        <div className={"morphbar morphbar--" + mode}>
          {/* BAR view content */}
          <div className="mb-bar" aria-hidden={mode !== 'bar'}>
            <div className="filterbar-row">
              <div className="fb-group">
                <span className="fb-label">Тип</span>
                <div className="fb-chips">
                  {types.map(t => (
                    <Chip key={t} active={type === t} onClick={() => setType(t)}>
                      {t === 'all' ? 'Все' : t}
                    </Chip>
                  ))}
                </div>
              </div>
              <div className="fb-group">
                <span className="fb-label">Гости</span>
                <div className="fb-chips">
                  {guestOpts.map(g => (
                    <Chip key={g} active={guests === g} onClick={() => setGuests(g)}>
                      {g === 0 ? 'Все' : g + '+'}
                    </Chip>
                  ))}
                </div>
              </div>
              <div className="fb-group fb-grow">
                <span className="fb-label">ЖК</span>
                <select className="fb-select" value={building} onChange={e => setBuilding(e.target.value)}>
                  {buildings.map(b => (
                    <option key={b} value={b}>{b === 'all' ? 'Любой жилой комплекс' : b}</option>
                  ))}
                </select>
              </div>
              <div className="fb-group">
                <span className="fb-label">Сортировка</span>
                <select className="fb-select" value={sort} onChange={e => setSort(e.target.value)}>
                  <option value="featured">Рекомендуемые</option>
                  <option value="price-asc">Цена ↑</option>
                  <option value="price-desc">Цена ↓</option>
                  <option value="area-desc">Площадь ↓</option>
                </select>
              </div>
            </div>
            <div className="filterbar-row">
              <RangeField label="Цена за ночь" suffix="₸" min={8000} max={28000} step={500}
                value={price} onChange={setPrice} />
              <RangeField label="Площадь" suffix="м²" min={28} max={75} step={1}
                value={area} onChange={setArea} />
              <button className="fb-reset" onClick={reset}>Сбросить</button>
            </div>
          </div>

          {/* RAIL view content */}
          <div className="mb-rail" aria-hidden={mode !== 'rail'}>
            <div className="mb-rail-inner">
            <div className="rail-mark">
              <span className="rail-mark-num">{filtered.length}</span>
              <span className="rail-mark-rule"></span>
              <span className="rail-mark-lbl">найдено</span>
            </div>

            <div className="rail-section">
              <div className="rail-vlabel">Тип</div>
              <div className="rail-stack">
                {types.map(t => (
                  <Chip key={t} active={type === t} onClick={() => setType(t)} vertical>
                    {t === 'all' ? 'Все' : t}
                  </Chip>
                ))}
              </div>
            </div>

            <div className="rail-section">
              <div className="rail-vlabel">Гости</div>
              <div className="rail-stack">
                {guestOpts.map(g => (
                  <Chip key={g} active={guests === g} onClick={() => setGuests(g)} vertical>
                    {g === 0 ? 'Все' : g + '+'}
                  </Chip>
                ))}
              </div>
            </div>

            <div className="rail-section">
              <div className="rail-vlabel">Цена</div>
              <RangeField label="" suffix="₸" min={8000} max={28000} step={500}
                value={price} onChange={setPrice} compact />
            </div>

            <div className="rail-section">
              <div className="rail-vlabel">Площадь</div>
              <RangeField label="" suffix="м²" min={28} max={75} step={1}
                value={area} onChange={setArea} compact />
            </div>

            <div className="rail-section rail-section-sel">
              <div className="rail-vlabel">ЖК</div>
              <select className="rail-select" value={building} onChange={e => setBuilding(e.target.value)}>
                {buildings.map(b => (
                  <option key={b} value={b}>{b === 'all' ? 'Любой' : b}</option>
                ))}
              </select>
            </div>

            <div className="rail-section rail-section-sel">
              <div className="rail-vlabel">Сорт.</div>
              <select className="rail-select" value={sort} onChange={e => setSort(e.target.value)}>
                <option value="featured">Реком.</option>
                <option value="price-asc">Цена ↑</option>
                <option value="price-desc">Цена ↓</option>
                <option value="area-desc">Площадь ↓</option>
              </select>
            </div>

            <button className="rail-reset" onClick={reset} title="Сбросить фильтры">
              <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4">
                <path d="M3 4l1.5 9h7L13 4M2 4h12M6 4V2.5a1 1 0 011-1h2a1 1 0 011 1V4" />
              </svg>
              {activeCount > 0 && <span className="rail-reset-dot">{activeCount}</span>}
            </button>
            </div>
          </div>
        </div>

        {heroSearch && (
          <div className="hero-search-chip">
            <span className="hsc-eyebrow">Ваш поиск</span>
            <span className="hsc-divider"></span>
            <span className="hsc-pill">{heroSearch.datesLabel || `${heroSearch.checkInLabel} — ${heroSearch.checkOutLabel}`}</span>
            <span className="hsc-pill">{heroSearch.guestsLabel}</span>
            <span className="hsc-pill">{heroSearch.districtLabel}</span>
            {heroSearch.typeLabel && heroSearch.type !== 'daily' && (
              <span className="hsc-pill">{heroSearch.typeLabel}</span>
            )}
            <button className="hsc-clear" onClick={() => { setHeroSearch(null); setDistrict('all'); setGuests(0); }} aria-label="Сбросить поиск">×</button>
          </div>
        )}

        <div className="catalog-result">
          <span className="cr-num">{filtered.length}</span>
          <span className="cr-rule"></span>
          <span className="cr-text">{
            filtered.length === 0 ? 'ничего не найдено — измените фильтры' :
            filtered.length === 1 ? 'апартамент найден' :
            filtered.length < 5 ? 'апартамента найдено' : 'апартаментов найдено'
          }</span>
        </div>

        <div className="apt-grid" ref={gridRef}>
          {filtered.map(apt => (
            <ApartmentCard key={apt.id} apt={apt} onOpen={onOpen} />
          ))}
        </div>
        {filtered.length === 0 && (
          <div className="apt-empty">
            <p>По выбранным фильтрам ничего не найдено.</p>
            <button onClick={reset}>Сбросить фильтры</button>
          </div>
        )}
      </div>
    </section>
  );
}

window.Catalog = Catalog;
