/* ============================================================
   Paella — игровая система «фуди»: баллы, уровни, ачивки.
   Баллы НЕ хранятся отдельным счётчиком — всегда пересчитываются
   из реальной истории (отзывы/посещения в VisitedStore + сохранёнки).
   Плюсы: нельзя потерять/накрутить правкой счётчика, синхронизация
   с аккаунтом бесплатно (история уже в /api/me/state).
   Вес отзыва в Paella Score растёт с уровнем (weight пишется в отзыв;
   живой пересчёт публичного балла включится на постоянном бэкенде).
   ============================================================ */

/* ---- начисления ---- */
const PTS = {
  review: 10,        // оценка блюда в один тап
  text: 5,           // + текст от 20 символов
  photo: 10,         // + своё фото
  params: 5,         // + шкалы 5 параметров
  newDish: 15,       // добавил блюдо, которого не было в меню
  visit: 3,          // отметка «я здесь был»
};

/* очки за один отзыв (для тостов «+25 баллов») */
function pointsForReview(r) {
  let p = PTS.review;
  if ((r.text || "").trim().length >= 20) p += PTS.text;
  if (r.userPhoto) p += PTS.photo;
  if (r.params && Object.values(r.params).some(v => v)) p += PTS.params;
  if (r.isNewDish) p += PTS.newDish;
  return p;
}

/* ---- медальоны «Кондитерская глазурь» (выбор Антона, дизайн-ревью №2):
   сквиркл как у iOS-иконок, слоёный градиент, узор, сияние, золотые искры ---- */
const G_PAL = {
  coral: ["#ffa26b", "#e04b16"], gold: ["#ffd35e", "#e39100"], lime: ["#bede63", "#5c8a1e"],
  teal: ["#67d6c2", "#128a7a"], blue: ["#7cbaf7", "#2d67ad"], berry: ["#f795bd", "#c23a76"],
  // алиасы старой палитры (на случай ссылок)
  olive: ["#bede63", "#5c8a1e"], plum: ["#f795bd", "#c23a76"],
};
let _gid = 0;
function GIcon({ icon, pal = "lime", size = 52, locked, motif = "rays" }) {
  const nRef = useRef(++_gid);
  const n = nRef.current;
  const c = locked ? ["#d6dae0", "#98a0ab"] : (G_PAL[pal] || G_PAL.lime);
  const idg = "cg" + n, idsheen = "cs" + n, idglow = "cw" + n, idclip = "cc" + n, idpat = "cp" + n, idblur = "cb" + n;
  const pathEls = (extra) => (ICONS[icon] || ICONS.sparkles).split("|").map((d, i) => <path key={i} d={d} transform={extra} />);
  const op = locked ? .05 : .09;
  let rays = null;
  if (motif === "rays") {
    rays = [];
    for (let i = 0; i < 12; i++) {
      const a = i * 30 * Math.PI / 180;
      rays.push(<line key={i} x1="60" y1="58" x2={60 + Math.cos(a) * 70} y2={58 + Math.sin(a) * 70} stroke="#fff" strokeOpacity={locked ? .04 : .07} strokeWidth="7" />);
    }
  }
  return (
    <svg width={size} height={size} viewBox="0 0 120 120" aria-hidden="true">
      <defs>
        <linearGradient id={idg} x1="0" y1="0" x2=".85" y2="1"><stop offset="0" stopColor={c[0]} /><stop offset="1" stopColor={c[1]} /></linearGradient>
        <linearGradient id={idsheen} x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor="#fff" stopOpacity={locked ? .18 : .42} /><stop offset="1" stopColor="#fff" stopOpacity="0" /></linearGradient>
        <radialGradient id={idglow} cx="50%" cy="46%" r="52%"><stop offset="0" stopColor="#fff" stopOpacity={locked ? .10 : .30} /><stop offset="1" stopColor="#fff" stopOpacity="0" /></radialGradient>
        <clipPath id={idclip}><rect x="10" y="10" width="100" height="100" rx="34" /></clipPath>
        <filter id={idblur} x="-40%" y="-40%" width="180%" height="180%"><feGaussianBlur stdDeviation="4.5" /></filter>
        {motif === "stripes" && <pattern id={idpat} width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(-24)"><rect width="8" height="16" fill="#fff" opacity={op} /></pattern>}
        {motif === "dots" && <pattern id={idpat} width="18" height="18" patternUnits="userSpaceOnUse"><circle cx="5" cy="5" r="2.2" fill="#fff" opacity={op} /><circle cx="14" cy="13" r="1.4" fill="#fff" opacity={op * 0.8} /></pattern>}
      </defs>
      <ellipse cx="60" cy="110" rx="36" ry="7" fill={c[1]} opacity={locked ? .18 : .38} filter={"url(#" + idblur + ")"} />
      <rect x="10" y="10" width="100" height="100" rx="34" fill={"url(#" + idg + ")"} />
      {motif === "rays" ? <g clipPath={"url(#" + idclip + ")"}>{rays}</g> : <rect x="10" y="10" width="100" height="100" rx="34" fill={"url(#" + idpat + ")"} />}
      <ellipse cx="60" cy="26" rx="46" ry="20" fill={"url(#" + idsheen + ")"} clipPath={"url(#" + idclip + ")"} />
      <circle cx="60" cy="58" r="34" fill={"url(#" + idglow + ")"} />
      <rect x="10" y="10" width="100" height="100" rx="34" fill="none" stroke="#fff8ec" strokeWidth="4" />
      <rect x="8" y="8" width="104" height="104" rx="36" fill="none" stroke="rgba(70,50,20,.10)" strokeWidth="1.5" />
      <g transform="translate(35 33) scale(2.05)" fill="none" stroke="#000" strokeOpacity=".26" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">{pathEls("translate(0 .7)")}</g>
      <g transform="translate(35 33) scale(2.05)" fill="none" stroke={locked ? "rgba(255,255,255,.6)" : "#fff"} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">{pathEls()}</g>
      {!locked && <>
        <path d="M95 14l2.6 6 6 2.6-6 2.6-2.6 6-2.6-6-6-2.6 6-2.6Z" fill="#fff" opacity=".95" />
        <path d="M22 92l1.8 4.2 4.2 1.8-4.2 1.8-1.8 4.2-1.8-4.2-4.2-1.8 4.2-1.8Z" fill="#ffe9b0" opacity=".95" />
        <circle cx="103" cy="78" r="3" fill="#ffe9b0" opacity=".9" />
      </>}
      {locked && <>
        <rect x="80" y="80" width="26" height="26" rx="9" fill="#fff" />
        <rect x="87" y="92" width="12" height="9" rx="1.8" fill="#77808c" />
        <path d="M89 92v-3a4 4 0 0 1 8 0v3" fill="none" stroke="#77808c" strokeWidth="2.2" strokeLinecap="round" />
      </>}
    </svg>
  );
}

/* ---- уровни ---- */
const LEVELS = [
  { at: 0, title: "Новичок", icon: "sprout", pal: "lime", motif: "dots" },
  { at: 50, title: "Дегустатор", icon: "utensils", pal: "lime", motif: "rays" },
  { at: 150, title: "Гурман", icon: "coffee", pal: "gold", motif: "dots" },
  { at: 300, title: "Знаток", icon: "wine", pal: "berry", motif: "stripes" },
  { at: 600, title: "Критик", icon: "edit", pal: "blue", motif: "stripes" },
  { at: 1000, title: "Шеф-фуди", icon: "chefhat", pal: "coral", motif: "rays" },
  { at: 2000, title: "Легенда Паэльи", icon: "crown", pal: "gold", motif: "rays" },
];

/* ---- ачивки: медальон-«глазурь» (палитра+узор = коллекция), условие, бонус ---- */
const ACHIEVEMENTS = [
  { id: "first",    icon: "edit",     pal: "lime",  motif: "rays",    title: "Первый отзыв",   desc: "Оставить первый отзыв",         goal: 1,  bonus: 10, count: s => s.reviews.length },
  { id: "photo5",   icon: "camera",   pal: "blue",  motif: "dots",    title: "Фотоохотник",    desc: "5 отзывов со своим фото",       goal: 5,  bonus: 25, count: s => s.reviews.filter(r => r.userPhoto).length },
  { id: "fire10",   icon: "flame",    pal: "coral", motif: "rays",    title: "Огонёк",         desc: "10 оценок «Взял бы ещё»",       goal: 10, bonus: 20, count: s => s.reviews.filter(r => r.rating === "fire").length },
  { id: "honest3",  icon: "shield",   pal: "teal",  motif: "stripes", title: "Честный критик", desc: "3 честных «Не зашло»",          goal: 3,  bonus: 15, count: s => s.reviews.filter(r => r.rating === "no").length },
  { id: "visit5",   icon: "flag",     pal: "berry", motif: "stripes", title: "Первопроходец",  desc: "Отметить 5 посещённых мест",    goal: 5,  bonus: 15, count: s => s.visits },
  { id: "visit15",  icon: "compass",  pal: "gold",  motif: "rays",    title: "Исследователь",  desc: "15 мест в Паспорте",            goal: 15, bonus: 30, count: s => s.visits },
  { id: "pasta3",   icon: "utensils", pal: "gold",  motif: "stripes", title: "Паста-охотник",  desc: "3 отзыва на пасту",             goal: 3,  bonus: 15, count: s => s.reviews.filter(r => /паст|спагетт|карбонар|лазань|равиол|ньокк/i.test(r.dish || "")).length },
  { id: "sushi3",   icon: "fish",     pal: "teal",  motif: "dots",    title: "Суши-сэнсэй",    desc: "3 отзыва на суши или роллы",    goal: 3,  bonus: 15, count: s => s.reviews.filter(r => /суши|ролл|сашими|нигири|филадельф/i.test(r.dish || "")).length },
  { id: "sweet3",   icon: "cake",     pal: "berry", motif: "dots",    title: "Сладкоежка",     desc: "3 отзыва на десерты",           goal: 3,  bonus: 15, count: s => s.reviews.filter(r => /десерт|чизкейк|тирамису|торт|мороже|эклер|наполеон|медовик|панна/i.test(r.dish || "")).length },
  { id: "scout3",   icon: "sparkles", pal: "coral", motif: "rays",    title: "Открыватель",    desc: "Добавить 3 новых блюда в базу", goal: 3,  bonus: 30, count: s => s.reviews.filter(r => r.isNewDish).length },
  { id: "days5",    icon: "calendar", pal: "lime",  motif: "dots",    title: "Марафонец",      desc: "Отзывы в 5 разных дней",        goal: 5,  bonus: 20, count: s => new Set(s.reviews.map(r => r.date).filter(Boolean)).size },
  { id: "save20",   icon: "bookmark", pal: "blue",  motif: "stripes", title: "Коллекционер",   desc: "20 сохранённых блюд и мест",    goal: 20, bonus: 15, count: s => s.savedCount },
  { id: "streak3",  icon: "trendUp",  pal: "coral", motif: "rays",    title: "Серийный фуди",  desc: "Отзывы 3 недели подряд",        goal: 3,  bonus: 30, count: s => s.streak },
  { id: "pastasub5",icon: "utensils", pal: "berry", motif: "rays",    title: "Пастоман",       desc: "5 разных видов пасты",          goal: 5,  bonus: 25, count: s => s.pastaSubs },
  { id: "subs10",   icon: "layers",   pal: "teal",  motif: "stripes", title: "Дегустатор вкусов", desc: "10 разных подвидов блюд",    goal: 10, bonus: 30, count: s => s.allSubs },
];

/* ---- снимок активности из сторов ---- */
const _wk = (ds) => {   // индекс ISO-недели (понедельник — начало) для 'YYYY-MM-DD'
  const [y, m, d] = (ds || "").split("-").map(Number);
  if (!y) return null;
  return Math.floor((Date.UTC(y, m - 1, d) / 86400000 + 3) / 7);
};
function gameSnapshot() {
  const P = window.PAELLA;
  const venues = VisitedStore.list();
  const reviews = [];
  venues.forEach(v => (v.reviews || []).forEach(r => reviews.push({ ...r, venueId: v.id })));
  let savedCount = 0;
  try { savedCount = (JSON.parse(localStorage.getItem("paella_saved_v1") || "[]") || []).length; } catch (e) {}
  savedCount += SavedRestsStore.count();

  // стрик: сколько недель ПОДРЯД есть хотя бы один отзыв (текущая неделя держит серию)
  const weeks = new Set(reviews.map(r => _wk(r.date)).filter(Boolean));
  const now = new Date();
  const thisWeek = Math.floor((Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) / 86400000 + 3) / 7);
  let streak = 0, w = weeks.has(thisWeek) ? thisWeek : thisWeek - 1;
  while (weeks.has(w)) { streak++; w--; }

  // подвиды попробованных блюд (отзыв → блюдо каталога по имени и заведению)
  const allSubs = new Set(), pastaSubs = new Set();
  if (P && P.dishes) {
    reviews.forEach(r => {
      const d = P.dishes.find(x => x.restaurantId === r.venueId && x.name === r.dish);
      if (d && d.sub) { allSubs.add(d.cat + "|" + d.sub); if (d.cat === "Паста") pastaSubs.add(d.sub); }
    });
  }
  return { reviews, visits: venues.length, savedCount, streak, allSubs: allSubs.size, pastaSubs: pastaSubs.size };
}

function gameState() {
  const s = gameSnapshot();
  const ach = ACHIEVEMENTS.map(a => {
    const cur = Math.min(a.goal, a.count(s));
    return { ...a, cur, done: cur >= a.goal };
  });
  let points = s.visits * PTS.visit;
  s.reviews.forEach(r => { points += pointsForReview(r); });
  ach.forEach(a => { if (a.done) points += a.bonus; });
  // серверные бонусы (рефералы и пр.) — карма аккаунта
  try { points += (AuthStore.user() && AuthStore.user().karma) || 0; } catch (e) {}
  let li = 0;
  for (let i = 0; i < LEVELS.length; i++) if (points >= LEVELS[i].at) li = i;
  const level = { n: li + 1, ...LEVELS[li] };
  const next = LEVELS[li + 1] || null;
  const progress = next ? Math.min(100, Math.round(((points - LEVELS[li].at) / (next.at - LEVELS[li].at)) * 100)) : 100;
  return { points, level, next, progress, ach, snapshot: s };
}

/* вес отзыва в Paella Score: растёт с уровнем (записывается в отзыв) */
function reviewWeight() {
  return Math.round((1 + (gameState().level.n - 1) * 0.15) * 100) / 100;   // ур.1 → 1.0 … ур.7 → 1.9
}

/* ---------- карточка уровня (профиль) ---------- */
function LevelCard() {
  useVisited(); useSavedRests();   // подписка на изменения
  const g = gameState();
  return (
    <div className="lvl-card">
      <div className="lvl-head">
        <GIcon icon={g.level.icon} pal={g.level.pal} motif={g.level.motif} size={56} />
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="lvl-title">{g.level.title} <span className="lvl-n">уровень {g.level.n}</span></div>
          <div className="lvl-sub">{g.points.toLocaleString("ru-RU")} баллов · вес отзыва ×{reviewWeight().toFixed(2)}{g.snapshot.streak > 0 ? <> · серия {g.snapshot.streak} нед 🔥</> : null}</div>
        </div>
        <div className="lvl-pts">{g.points}</div>
      </div>
      <div className="lvl-bar"><div className="lvl-fill" style={{ width: g.progress + "%" }} /></div>
      <div className="lvl-next" style={{ display: "flex", alignItems: "center", gap: 7 }}>
        {g.next
          ? <>До «{g.next.title}» <GIcon icon={g.next.icon} pal={g.next.pal} motif={g.next.motif} size={20} /> — ещё <b>{g.next.at - g.points}</b> баллов</>
          : <>Максимальный уровень — ты Легенда Паэльи 🥘</>}
      </div>
      <div className="lvl-how">
        <span>＋{PTS.review} отзыв</span><span>＋{PTS.photo} фото</span><span>＋{PTS.text} текст</span>
        <span>＋{PTS.params} шкалы</span><span>＋{PTS.newDish} новое блюдо</span><span>＋{PTS.visit} «я здесь был»</span>
      </div>
    </div>
  );
}

/* ---------- сетка ачивок ---------- */
function AchievementsGrid() {
  useVisited(); useSavedRests();
  const g = gameState();
  const unlocked = g.ach.filter(a => a.done).length;
  return (
    <section className="sec">
      <div className="sec-head">
        <div>
          <h2 className="sec-title">Достижения</h2>
          <p className="sec-sub">{unlocked} из {g.ach.length} открыто · каждая даёт бонусные баллы</p>
        </div>
      </div>
      <div className="ach-grid">
        {g.ach.map(a => (
          <div key={a.id} className={"ach" + (a.done ? " done" : "")} title={a.desc + (a.done ? " — открыто!" : "")}>
            <GIcon icon={a.icon} pal={a.pal} motif={a.motif} size={58} locked={!a.done} />
            <span className="t">{a.title}</span>
            {a.done
              ? <span className="st ok">+{a.bonus} баллов ✓</span>
              : <span className="st">{a.cur} / {a.goal}</span>}
            {!a.done && <span className="ach-bar"><span style={{ width: Math.round((a.cur / a.goal) * 100) + "%" }} /></span>}
          </div>
        ))}
      </div>
    </section>
  );
}

/* ---------- салют «Новая ачивка»: подъём + Apple-вращение + звёздный залп ----------
   Детектор: следим за сторами; новая done-ачивка → событие paella-ach.
   Дождь конфетти убран по фидбеку Антона — только вспышка, вращение и искры. */
const ACH_SEEN_KEY = "paella_ach_seen_v1";
(function watchAchievements() {
  let seen = null;
  try { seen = JSON.parse(localStorage.getItem(ACH_SEEN_KEY) || "null"); } catch (e) {}
  const doneNow = () => gameState().ach.filter(a => a.done).map(a => a.id);
  if (!Array.isArray(seen)) {   // первый запуск: не салютуем задним числом
    seen = doneNow();
    try { localStorage.setItem(ACH_SEEN_KEY, JSON.stringify(seen)); } catch (e) {}
  }
  const check = () => {
    const now = doneNow();
    const fresh = now.filter(id => !seen.includes(id));
    if (fresh.length) {
      seen = now;
      try { localStorage.setItem(ACH_SEEN_KEY, JSON.stringify(seen)); } catch (e) {}
      const a = gameState().ach.find(x => x.id === fresh[0]);
      if (a) setTimeout(() => window.dispatchEvent(new CustomEvent("paella-ach", { detail: a })), 450);
    }
  };
  VisitedStore.subscribe(check);
  SavedRestsStore.subscribe(check);
})();

function AchCelebration() {
  const [ach, setAch] = useState(null);
  useEffect(() => {
    const h = (e) => setAch(e.detail);
    window.addEventListener("paella-ach", h);
    return () => window.removeEventListener("paella-ach", h);
  }, []);
  if (!ach) return null;
  const stars = [];
  for (let i = 0; i < 14; i++) {
    const a = i * (360 / 14), big = i % 3 === 0;
    // зажигаются по кругу друг за другом — бегущий огонёк вокруг обода
    stars.push(<span key={i} className="celeb-star" style={{ "--a": a + "deg", "--d": (big ? 52 : 34) + "px", animationDelay: (0.58 + i * 0.028) + "s", fontSize: big ? 16 : 11 }}>{i % 2 ? "✦" : "★"}</span>);
  }
  return (
    <div className="celeb-overlay" onClick={() => setAch(null)}>
      <div className="celeb-box" onClick={(e) => e.stopPropagation()}>
        <div className="celeb-ring" />
        <div className="celeb-medal"><span className="celeb-spin"><GIcon icon={ach.icon} pal={ach.pal} motif={ach.motif} size={150} /></span></div>
        {stars}
        <div className="celeb-eyebrow">Новая ачивка!</div>
        <div className="celeb-title">{ach.title}</div>
        <div className="celeb-desc">{ach.desc}</div>
        <div className="celeb-pts">+{ach.bonus} баллов 🔥</div>
        <button className="btn btn-pri btn-sm" onClick={() => setAch(null)}>Огонь!</button>
      </div>
    </div>
  );
}

Object.assign(window, { LevelCard, AchievementsGrid, AchCelebration, pointsForReview, gameState, reviewWeight, PAELLA_LEVELS: LEVELS });
