// 引导式 tour — 用户首次登录后, 刘看山一步一步带着体验镜像回答.
//
// 7 张 onboarding PNG (刘看山 + 异形对话框 + 知乎蓝高亮关键字 一体) 已落在
// app/assets/onboarding/, 不再需要自己拼"圆头 emoji + speech bubble".
//
// 主流程 7 步 (用户问减肥相关问题):
//   #1 入场:                     "点亮你的手机屏幕吧"               → 按左屏电源键
//   #2 知乎 splash 完进主页:      "帮我问一个减肥相关的问题吗?"      → 点 + 提问
//   #3 发布完, invite list 出现:  "有一个镜像回答的按钮, 点一下试试"  → 点镜像回答
//   #4 镜像答主卡片出来:          "给一个答案点个赞, 看看会发生什么"  → 任意点赞
//   #5 右屏 +1 自动点完, 显示更新选项: "要不要去更新一下最新的回答?" → 点"更新我的回答"
//   #6 新答案插入 mirror panel:    "滑到最底下, 看看我是怎么总结的"   → 滑到 synthesis
//   #7 停在 synthesis ≥ 2s:        "完全了解这个功能了!"              → × 进入自由探索
//
// 状态机本身只追踪"当前停在哪个 step", 推进由用户的真实 action 驱动.

const TOUR_STEPS = {
  IDLE:                  'idle',
  P1_LIGHT_SCREEN:       'p1-light-screen',
  P2_STARRY_ASK:         'p2-starry-ask',
  P3_TRY_MIRROR_BUTTON:  'p3-try-mirror-button',
  P4_TRY_LIKE:           'p4-try-like',
  P5_UPDATE_ANSWER:      'p5-update-answer',
  P6_SCROLL_TO_BOTTOM:   'p6-scroll-bottom',
  P7_DONE:               'p7-done',
  // 离题分支: 用户问了非减肥问题时走这条线
  P3_OFFTRACK:           'p3-offtrack',         // popup-8: 你有独特的想法, 点击镜像回答
  P4_OFFTRACK:           'p4-offtrack-mystery', // popup-9: 数据库没这个, 看看推给了谁
  P5_OFFTRACK:           'p5-offtrack-return',  // popup-10: 回到首页重新提一个减肥
};

const TOUR_DONE_KEY = 'tour_completed_v1';

function hasTourBeenCompleted() {
  try { return localStorage.getItem(TOUR_DONE_KEY) === '1'; } catch { return false; }
}
function markTourCompleted() {
  try { localStorage.setItem(TOUR_DONE_KEY, '1'); } catch {}
}
function resetTour() {
  try { localStorage.removeItem(TOUR_DONE_KEY); } catch {}
}

// step → onboarding PNG 文件名 + 实际像素尺寸. 把 width/height 写在配置里, 这样
// img 上能挂 aspect-ratio, 浏览器拿到 <img> 标签的瞬间就知道这块区域多高,
// 不会出现"先以 0 高度渲染 → translate(-50%, -50%) 把弹窗钉在 65% 位置 → 图加载好
// 才扩展高度 → translate 重算 → 跳到中间"的位置闪跳.
const TOUR_POPUPS = {
  [TOUR_STEPS.P1_LIGHT_SCREEN]:      { img: 'assets/onboarding/popup-1-light.png',    alt: '点亮你的手机屏幕吧',                  w: 2480, h: 1069 },
  [TOUR_STEPS.P2_STARRY_ASK]:        { img: 'assets/onboarding/popup-2-starry.png',   alt: '帮我问一个减肥相关的问题吗',          w: 2480, h: 1075 },
  [TOUR_STEPS.P3_TRY_MIRROR_BUTTON]: { img: 'assets/onboarding/popup-3-mirror.png',   alt: '有一个镜像回答的按钮, 点一下试试',    w: 2290, h: 1038 },
  [TOUR_STEPS.P4_TRY_LIKE]:          { img: 'assets/onboarding/popup-4-like.png',     alt: '试一试给其中一个答案点个赞',          w: 2415, h: 1198 },
  [TOUR_STEPS.P5_UPDATE_ANSWER]:     { img: 'assets/onboarding/popup-5-update.png',   alt: '要不要去更新一下最新的回答',          w: 2478, h: 1091 },
  [TOUR_STEPS.P6_SCROLL_TO_BOTTOM]:  { img: 'assets/onboarding/popup-6-scroll.png',   alt: '滑到最底下看看刘看山的总结',          w: 2480, h: 1106 },
  [TOUR_STEPS.P7_DONE]:              { img: 'assets/onboarding/popup-7-done.png',     alt: '完全了解这个功能了',                  w: 2456, h: 1086 },
  // 离题分支 3 张 PNG, w/h 是 trim+黑底转透明后的实际尺寸. fallbackText 留着以防
  // 图片加载失败 (onError) — 平时不会触发.
  [TOUR_STEPS.P3_OFFTRACK]:          { img: 'assets/onboarding/popup-8-offtrack.png', alt: '你有独特的想法, 点击镜像回答',        w: 2341, h: 863,
    fallbackText: ['诶? 这个不是减肥话题呢, 你的脑洞真大!', '不过既然都问了 — 点一下「镜像回答」, 看看会发生什么 ✨'],
    fallbackHighlights: ['脑洞真大', '镜像回答'] },
  [TOUR_STEPS.P4_OFFTRACK]:          { img: 'assets/onboarding/popup-9-mystery.png',  alt: '数据库不包含, 看看问题推给了谁',      w: 2238, h: 966,
    fallbackText: ['嘿, 我翻遍了答主库, 这题真的没人答过…', '不过别急! 我刚刚偷偷把它推给了一位「神秘大佬」', '我们一起看看, 是谁来了?'],
    fallbackHighlights: ['没人答过', '神秘大佬'] },
  [TOUR_STEPS.P5_OFFTRACK]:          { img: 'assets/onboarding/popup-10-return.png',  alt: '回到首页重新提一个减肥相关的问题吧',  w: 2319, h: 919,
    fallbackText: ['哇, 神秘答主真的能召唤来呢~', '这只是边界剧情啦 —', '回到首页, 提一个「减肥」相关的, 那边可精彩了'],
    fallbackHighlights: ['边界剧情', '减肥'] },
};

// 应用启动时一次性预载所有 PNG, 避免第一个弹窗刚出来时还在下载图.
// 之前用户反馈"硬刷之后第 1 个动画看不到" — 现在浏览器至少有这堆图在 cache 里,
// 即使 React state 直接跳到 P1, img 也是命中缓存秒出.
function preloadTourImages() {
  if (typeof window === 'undefined') return;
  if (window.__tourImgsPreloaded) return;
  window.__tourImgsPreloaded = [];
  for (const step of Object.keys(TOUR_POPUPS)) {
    const im = new Image();
    im.src = TOUR_POPUPS[step].img;
    window.__tourImgsPreloaded.push(im);
  }
}
// 模块装载时立刻发起预载 — 比等 Page mount 早一点点, 几张图同时下.
preloadTourImages();

// ─── 刘看山引导浮层 ───────────────────────────────────────────
// 整张图本身就是"刘看山 + 异形对话框 + 文字 + 关键字高亮", 我们只负责
// 放在合适位置 + 入场动画 + 提供关闭按钮.
//
// 位置策略: 默认在屏幕中下方 (top:65%, left:50%), 刚好不挡两台手机的关键
// 操作区. 个别 step (Popup 5 跟 +1 button 配套) 右偏 60vw.
const POPUP_POSITIONS = {
  [TOUR_STEPS.P5_UPDATE_ANSWER]: { left: '62%', top: '60%' },  // 偏右一点, 靠近右屏的"更新我的回答" 按钮
};

function LiukanshanDialog({ step, onDismiss }) {
  if (!step || step === TOUR_STEPS.IDLE) return null;
  const popup = TOUR_POPUPS[step];
  if (!popup) return null;
  const pos = POPUP_POSITIONS[step] || { left: '50%', top: '65%' };  // 默认中下偏
  // PNG 加载失败 → 走 HTML 气泡 fallback (用于 popup-8/9/10 用户还没出图时)
  const [imgFailed, setImgFailed] = React.useState(false);
  const useFallback = imgFailed && popup.fallbackText;

  return (
    <div style={{
      position: 'fixed',
      left: pos.left, top: pos.top,
      transform: 'translate(-50%, -50%)',
      zIndex: 200,
      pointerEvents: 'none',  // 容器不挡点击, 关闭键自己开 pointer
      // 单一入场动画: 原地 fade + scale, 不再有 translateY(20px) 的位移.
      // (用户反馈: 旧版的 translateY 看起来像"先出现在下面再跳到上面" — 现改成原地淡入.)
      // animation-fill-mode: both → 动画结束后保留终态, 不会闪一下.
      animation: 'lks-fade-in 380ms ease-out both',
    }}>
      <style>{`
        @keyframes lks-fade-in {
          0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
          100% { opacity: 1; transform: translate(-50%, -50%) scale(1);    }
        }
        @keyframes lks-bob {
          0%, 100% { transform: translateY(0); }
          50%      { transform: translateY(-2px); }
        }
      `}</style>

      <div style={{
        position: 'relative',
        // bob 延迟 500ms 启动, 等 lks-fade-in 跑完后再开始; 用户之前感觉"动画显示两次"
        // 就是入场动画跟 bob 几乎同时启动叠在一起造成的.
        animation: 'lks-bob 4s ease-in-out 500ms infinite',
        filter: 'drop-shadow(0 8px 24px rgba(0,0,0,0.18)) drop-shadow(0 1px 4px rgba(0,0,0,0.10))',
      }}>
        {useFallback ? (
          <FallbackBubble lines={popup.fallbackText} highlights={popup.fallbackHighlights || []}/>
        ) : (
          <img
            src={popup.img}
            alt={popup.alt}
            // width + height + aspectRatio 三件套一起写: 浏览器拿到标签就能算出最终
            // 占位区域, 不会因为图片晚到几十毫秒导致弹窗跳位.
            width={540}
            height={Math.round(540 * popup.h / popup.w)}
            onError={() => setImgFailed(true)}
            style={{
              display: 'block',
              width: 540,
              maxWidth: '92vw',
              height: 'auto',
              aspectRatio: `${popup.w} / ${popup.h}`,
              pointerEvents: 'auto',
              userSelect: 'none',
            }}
            draggable={false}
          />
        )}

        {/* 关闭键 — 浮在图右上角. 跟卡通风格契合, 小圆形描边. */}
        {onDismiss && (
          <div onClick={onDismiss} style={{
            position: 'absolute', top: -6, right: -6,
            width: 22, height: 22, borderRadius: 11,
            background: '#fff', color: '#7A8089',
            border: '1.5px solid #1A1B1F',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 13, lineHeight: 1, fontWeight: 600,
            cursor: 'pointer', userSelect: 'none',
            pointerEvents: 'auto',
            boxShadow: '0 2px 6px rgba(0,0,0,0.15)',
          }}>×</div>
        )}
      </div>
    </div>
  );
}

// ─── PNG 缺位时的 HTML 气泡 fallback ──────────────────────────────
// 风格: 白底 + 异形圆角 + 知乎蓝高亮关键词 + 一个圆形 "刘看山" 小头像
// (用 liukanshan-views.png 切第一个 view 当头像), 不需要外部图片资源.
function FallbackBubble({ lines = [], highlights = [] }) {
  const renderLine = (line, key) => {
    if (!highlights.length) return <span key={key}>{line}</span>;
    // 把每个 highlight 词标蓝
    const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const re = new RegExp('(' + highlights.map(escapeRe).join('|') + ')', 'g');
    const parts = line.split(re);
    return parts.map((p, i) => {
      if (highlights.includes(p)) {
        return <span key={key + '-' + i} style={{ color: '#1772F6', fontWeight: 700 }}>{p}</span>;
      }
      return <React.Fragment key={key + '-' + i}>{p}</React.Fragment>;
    });
  };
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-start', gap: 14,
      maxWidth: 500,
      padding: '20px 24px 22px 22px',
      background: '#FFF8E8',
      border: '3px solid #2B2D33',
      borderRadius: 28,
      boxShadow: '0 12px 36px rgba(0,0,0,0.22), 0 2px 8px rgba(0,0,0,0.12)',
      pointerEvents: 'auto',
      fontFamily: '"PingFang SC", "Noto Sans SC", system-ui',
      color: '#1A1B1F',
    }}>
      {/* 刘看山头像 (圆形, 从 4-view PNG 切第一格) */}
      <div style={{
        flexShrink: 0,
        width: 56, height: 56, borderRadius: '50%',
        background: '#F4F5F7',
        backgroundImage: 'url(assets/liukanshan-views.png)',
        backgroundSize: '400% 100%',
        backgroundPosition: '0% center',
        backgroundRepeat: 'no-repeat',
        border: '2px solid #1A1B1F',
      }}/>
      <div style={{ flex: 1, minWidth: 0, paddingTop: 2 }}>
        {lines.map((line, i) => (
          <div key={i} style={{
            fontSize: 14, lineHeight: 1.55,
            marginTop: i === 0 ? 0 : 6,
            fontWeight: i === 0 ? 700 : 500,
          }}>
            {renderLine(line, i)}
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, {
  TOUR_STEPS, TOUR_POPUPS, LiukanshanDialog,
  hasTourBeenCompleted, markTourCompleted, resetTour,
});
