/* ============================================================
   Paella — shared UI components
   Exposes components on window for other babel scripts.
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- icon set (lucide-style line icons) ---------- */
const ICONS = {
  search: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm10 2-4.35-4.35",
  pin: "M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z|M12 10m-3 0a3 3 0 1 0 6 0 3 3 0 1 0-6 0",
  star: "M12 2.5l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.8 6.1 20.5l1.2-6.5L2.5 9.4l6.6-.9L12 2.5Z",
  heart: "M20.8 5.6a5 5 0 0 0-7.1 0L12 7.3l-1.7-1.7a5 5 0 1 0-7.1 7.1l1.7 1.7L12 21.5l7.1-7.1 1.7-1.7a5 5 0 0 0 0-7.1Z",
  bookmark: "M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16Z",
  arrowRight: "M5 12h14|M13 5l7 7-7 7",
  arrowDown: "M12 5v14|M5 12l7 7 7-7",
  chevronRight: "M9 6l6 6-6 6",
  chevronLeft: "M15 6l-6 6 6 6",
  x: "M18 6 6 18|M6 6l12 12",
  home: "M3 10.5 12 3l9 7.5|M5 9.5V21h14V9.5",
  grid: "M3 3h7v7H3z|M14 3h7v7h-7z|M14 14h7v7h-7z|M3 14h7v7H3z",
  trophy: "M8 21h8|M12 17v4|M7 4h10v5a5 5 0 0 1-10 0V4Z|M7 4H4v2a3 3 0 0 0 3 3|M17 4h3v2a3 3 0 0 1-3 3",
  plus: "M12 5v14|M5 12h14",
  user: "M20 21a8 8 0 1 0-16 0|M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z",
  route: "M6 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z|M18 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z|M9 16h6a3 3 0 0 0 0-6H9a3 3 0 0 1 0-6",
  check: "M20 6 9 17l-5-5",
  flame: "M12 22c4 0 7-2.6 7-6.5 0-3.2-2.3-5.4-3.5-7.5-.6 1.2-1.4 1.8-2.3 1.8C13 6.5 14 3.5 11 2 11 6 7 8 7 13.5 7 18 8 22 12 22Z",
  sliders: "M4 6h16|M4 12h16|M4 18h16|M8 4v4|M16 10v4|M11 16v4",
  clock: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z|M12 7v5l3 2",
  wallet: "M3 7h16a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a1 1 0 0 1-1-1V7Z|M3 7l0-1a2 2 0 0 1 2-2h11v3|M17 13h.01",
  utensils: "M4 3v7a2 2 0 0 0 2 2h0a2 2 0 0 0 2-2V3|M6 12v9|M15 3c-1.5 1-2 3-2 5s.5 3 2 3h0v10",
  sparkles: "M12 3l1.8 4.7L18.5 9l-4.7 1.8L12 15l-1.8-4.2L5.5 9l4.7-1.3L12 3Z|M19 14l.8 2 .2.8 2-.8-2 .8-.2-.8-.8-2-.8 2-2 .8 2 .8.8 2 .8-2Z",
  trendUp: "M22 7 13.5 15.5 8.5 10.5 2 17|M16 7h6v6",
  message: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10Z",
  share: "M4 12v8a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-8|M16 6l-4-4-4 4|M12 2v14",
  shield: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z",
  target: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z|M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z|M12 12h.01",
  layers: "M12 2 2 7l10 5 10-5-10-5Z|M2 12l10 5 10-5|M2 17l10 5 10-5",
  coffee: "M4 8h13v5a5 5 0 0 1-5 5H9a5 5 0 0 1-5-5V8Z|M17 9h2a2 2 0 0 1 0 6h-1|M7 4V2|M11 4V2",
  moon: "M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z",
  sun: "M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10Z|M12 1v2|M12 21v2|M4.2 4.2l1.4 1.4|M18.4 18.4l1.4 1.4|M1 12h2|M21 12h2|M4.2 19.8l1.4-1.4|M18.4 5.6l1.4-1.4",
  flag: "M4 21V4|M4 4h11l-1.5 3.5L15 11H4",
  zoomIn: "M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm10 2-4.35-4.35|M11 8v6|M8 11h6",
  compass: "M12 21a9 9 0 1 0 0-18 9 9 0 0 0 0 18Z|M16 8l-2 6-6 2 2-6 6-2Z",
};

function Icon({ name, size = 20, sw = 1.9, fill = "none", style, className }) {
  const d = ICONS[name] || "";
  const parts = d.split("|");
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke="currentColor"
      strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" style={style} className={className}>
      {parts.map((p, i) => <path key={i} d={p} />)}
    </svg>
  );
}

/* ---------- SmartImage: real photo with graceful gradient+emoji fallback ---------- */
function SmartImage({ src, emoji, grad, alt, className, rounded }) {
  const [failed, setFailed] = useState(false);
  const g = grad || ["oklch(0.78 0.13 55)", "oklch(0.66 0.16 35)"];
  return (
    <div className={className} style={{ position: "absolute", inset: 0, background: `linear-gradient(150deg, ${g[0]}, ${g[1]})` }}>
      {!failed && src && (
        <img src={src} alt={alt || ""} referrerPolicy="no-referrer"
          onError={() => setFailed(true)}
          style={{ width: "100%", height: "100%", objectFit: "cover", position: "relative", zIndex: 1 }} />
      )}
      {failed && (
        <div className="imgph"><span className="e">{emoji || "🍽️"}</span></div>
      )}
    </div>
  );
}

/* ---------- Badge ---------- */
function Badge({ children, cls }) {
  return <span className={"badge " + (cls || "b-sand")}>{children}</span>;
}

/* ---------- ScoreBadge ---------- */
function ScoreBadge({ value, size, float }) {
  return (
    <span className={"score" + (size === "lg" ? " lg" : "") + (float ? " float" : "")}>
      <span className="st"><Icon name="star" size={size === "lg" ? 16 : 13} fill="currentColor" sw={0} /></span>
      {value.toFixed(1)}
    </span>
  );
}

/* ============================================================
   Paella Score 2.0 — единый бейдж балла (шкала 0–100).
   Цвет = уровень балла, иконка = источник данных:
   ❤ оценки гостей · 💬 отзывы · ✦ прогноз Paella (мало данных → «~»).
   Модель и расчёт — в paella-data.js (window.paellaScore).
   ============================================================ */
const KIND_ICON = { guest: "heart", review: "message", ai: "sparkles" };
/* собрать объект балла из готовых полей блюда-из-каталога (adapter dish) */
const uOf = (d) => ({ score: d.score, kind: d.kind, approx: d.scoreApprox, votes: d.ratingCount || 0, mentions: d.reviewMentions || 0 });
function ScoreTag({ u, float, lg, style }) {
  const title = u.kind === "guest" ? `Paella Score ${u.score} — по реальным оценкам гостей (${u.votes})`
    : u.kind === "review" ? `Paella Score ${u.score} — по упоминаниям блюда в отзывах (${u.mentions})`
    : `Paella Score ~${u.score} — прогноз: реальных оценок пока мало`;
  return (
    <span className={"score" + (lg ? " lg" : "") + (float ? " float" : "")}
      style={{ background: scoreColor(u.score), color: "#fff", ...(style || {}) }} title={title}>
      <Icon name={KIND_ICON[u.kind] || "sparkles"} size={lg ? 14 : 11} fill="currentColor" sw={0} />
      {u.approx ? "~" : ""}{u.score}
    </span>
  );
}
/* подпись-источник под баллом */
function scoreCaption(u) {
  return u.kind === "guest" ? `${u.votes} оценок гостей` : u.kind === "review" ? `${u.mentions} в отзывах` : "прогноз Paella";
}

/* ---------- avatar color from name ---------- */
const AV_COLORS = [
  "linear-gradient(140deg, oklch(0.7 0.16 35), oklch(0.62 0.17 20))",
  "linear-gradient(140deg, oklch(0.72 0.13 150), oklch(0.62 0.14 170))",
  "linear-gradient(140deg, oklch(0.7 0.13 280), oklch(0.6 0.14 300))",
  "linear-gradient(140deg, oklch(0.74 0.14 60), oklch(0.66 0.15 40))",
  "linear-gradient(140deg, oklch(0.68 0.12 230), oklch(0.6 0.13 250))",
];
const avColor = (name) => AV_COLORS[(name.charCodeAt(0) + (name.charCodeAt(1) || 0)) % AV_COLORS.length];

/* ============================================================
   Паспорт мест — посещённые рестораны + отзывы (localStorage).
   Глобальный observable-стор, чтобы любой компонент мог отметить
   посещение / добавить отзыв без проп-дриллинга.
   ============================================================ */
const VisitedStore = (function () {
  const KEY = "paella_visited_v1";
  let data = {};
  try { data = JSON.parse(localStorage.getItem(KEY) || "{}"); } catch (e) { data = {}; }
  const subs = new Set();
  const save = () => { try { localStorage.setItem(KEY, JSON.stringify(data)); } catch (e) {} subs.forEach(f => f()); };
  const base = (r) => ({ id: r.id, name: r.name, district: r.district, cuisine: r.cuisine, lat: r.latitude, lng: r.longitude, imageUrl: r.imageUrl, address: r.address || "", rating: r.rating != null ? r.rating : null, visitedAt: Date.now(), reviews: [] });
  const ensure = (r) => { if (!data[r.id]) data[r.id] = base(r); return data[r.id]; };
  return {
    all: () => data,
    list: () => Object.values(data).sort((a, b) => (b.visitedAt || 0) - (a.visitedAt || 0)),
    isVisited: (id) => !!data[id],
    count: () => Object.keys(data).length,
    reviewCount: () => Object.values(data).reduce((n, v) => n + (v.reviews ? v.reviews.length : 0), 0),
    toggle: (r) => { if (data[r.id]) delete data[r.id]; else ensure(r); save(); },
    visit: (r) => { ensure(r); save(); },
    remove: (id) => { delete data[id]; save(); },
    addReview: (r, review) => { const v = ensure(r); v.reviews = v.reviews || []; v.reviews.unshift({ ...review, ts: Date.now() }); save(); },
    subscribe: (f) => { subs.add(f); return () => subs.delete(f); },
  };
})();
function useVisited() {
  const [, force] = useState(0);
  useEffect(() => VisitedStore.subscribe(() => force(x => x + 1)), []);
  return VisitedStore;
}

/* ---------- robust smooth window scroll (behavior:"smooth" is a no-op in some webviews) ---------- */
function paellaScrollTo(targetY, dur = 420) {
  const start = window.pageYOffset || document.documentElement.scrollTop || 0;
  const target = Math.max(0, targetY);
  if (Math.abs(target - start) < 2) { window.scrollTo(0, target); return; }
  const t0 = performance.now();
  const ease = (t) => 1 - Math.pow(1 - t, 3);
  const tick = (now) => {
    const p = Math.min(1, (now - t0) / dur);
    window.scrollTo(0, start + (target - start) * ease(p));
    if (p < 1) requestAnimationFrame(tick);
  };
  requestAnimationFrame(tick);
}

/* ---------- DishCard (реальные данные: фото-или-плейсхолдер, % гостей / AI) ---------- */
function DishCard({ dish, saved, onSave, onOpen }) {
  const u = uOf(dish);
  return (
    <article className="dcard" onClick={() => onOpen(dish)}>
      <div className="dcard-media">
        <DishPhoto src={dish.photo} alt={dish.name} />
        <ScoreTag u={u} float />
        <button className={"dcard-save" + (saved ? " on" : "")}
          onClick={(e) => { e.stopPropagation(); onSave(dish.id); }} aria-label="Сохранить">
          <Icon name="bookmark" size={18} fill={saved ? "currentColor" : "none"} />
        </button>
      </div>
      <div className="dcard-body">
        <div className="dcard-name">{dish.name}</div>
        <div className="dcard-meta">
          <span style={{ fontWeight: 600, color: "var(--ink)", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{dish.restaurantName}</span>
          <span className="dot"></span><span>{dish.district}</span>
        </div>
        {dish.reviewSnippet ? (
          <div className="dcard-reason" style={{ fontStyle: "italic" }}>«{dish.reviewSnippet}»</div>
        ) : (dish.grams || dish.kcal) ? (
          <div className="dcard-reason" style={{ color: "var(--ink-3)" }}>{[dish.grams && `${dish.grams} г`, dish.kcal && `${dish.kcal} ккал`].filter(Boolean).join(" · ")}</div>
        ) : dish.desc ? (
          <div className="dcard-reason">{dish.desc}</div>
        ) : <div className="dcard-reason">&nbsp;</div>}
        <div className="dcard-foot">
          <span className="dcard-price">{dish.price ? dish.price : "—"} <span className="cur">₽</span></span>
          <span className="muted" style={{ fontSize: 12.5, fontWeight: 600, display: "flex", alignItems: "center", gap: 4 }}><Icon name={KIND_ICON[u.kind]} size={12} />{scoreCaption(u)}</span>
        </div>
      </div>
    </article>
  );
}

/* ---------- EmptyState ---------- */
function EmptyState({ emoji, title, text }) {
  return (
    <div className="empty">
      <div className="e">{emoji || "🍽️"}</div>
      <h4>{title}</h4>
      <p style={{ maxWidth: 360, margin: "0 auto" }}>{text}</p>
    </div>
  );
}

/* ---------- CTA button ---------- */
function CTA({ kind = "pri", icon, children, onClick }) {
  return (
    <button className={"btn btn-" + kind} onClick={onClick}>
      {icon && <Icon name={icon} size={18} />}{children}
    </button>
  );
}

Object.assign(window, {
  Icon, SmartImage, Badge, ScoreBadge, ScoreTag, scoreCaption, uOf, KIND_ICON, DishCard, EmptyState, CTA, avColor, paellaScrollTo,
  VisitedStore, useVisited,
  // expose hooks globally so other Babel scripts resolve them via the global object
  useState, useEffect, useRef, useMemo,
});
