/* ═══════════════════════════════════════════════════════════════
   Fortune Sticks — Game Logic
   State flow: landing → shaking → flying → result
   ═══════════════════════════════════════════════════════════════ */

// ── State ────────────────────────────────────────────────────────
const state = {
  phase: 'landing',     // landing | shaking | flying | result
  shakeAccum: 0,        // accumulated shake energy (0-100)
  lastShakeTs: 0,
  motionEnabled: false,
  drawn: false,
};

// ── DOM ──────────────────────────────────────────────────────────
const pageLanding    = document.getElementById('pageLanding');
const pageShake      = document.getElementById('pageShake');
const btnStart       = document.getElementById('btnStart');
const btnShake       = document.getElementById('btnShake');
const btnCta         = document.getElementById('btnCta');
const sticksLanding  = document.getElementById('sticksLanding');
const sticksShake    = document.getElementById('sticksShake');
const vessel         = document.getElementById('vessel');
const shakeBar       = document.getElementById('shakeBar');
const shakeHint      = document.getElementById('shakeHint');
const flyingStick    = document.getElementById('flyingStick');
const resultOverlay  = document.getElementById('resultOverlay');
const resultCard     = document.getElementById('resultCard');

// ── Audio (Howler.js) — graceful if files missing ────────────────
function makeSound(src, opts = {}) {
  try {
    return new Howl({ src: [src], volume: 0.6, ...opts });
  } catch { return { play: () => {} }; }
}
const sfx = {
  shake:   makeSound('assets/audio/shake.mp3',   { loop: true, volume: 0.5 }),
  crack:   makeSound('assets/audio/crack.mp3'),
  reveal:  makeSound('assets/audio/reveal.mp3'),
  click:   makeSound('assets/audio/click.mp3',   { volume: 0.4 }),
};

// ── Particle system ──────────────────────────────────────────────
(function initParticles() {
  const canvas = document.getElementById('particleCanvas');
  const ctx    = canvas.getContext('2d');
  let particles = [];

  function resize() {
    canvas.width  = window.innerWidth;
    canvas.height = window.innerHeight;
  }
  window.addEventListener('resize', resize);
  resize();

  function makeParticle() {
    return {
      x:       Math.random() * canvas.width,
      y:       canvas.height + 8,
      size:    Math.random() * 2.5 + 0.5,
      speed:   Math.random() * 0.6 + 0.2,
      drift:   (Math.random() - 0.5) * 0.4,
      opacity: Math.random() * 0.5 + 0.15,
      hue:     40 + Math.random() * 20,   // gold range
    };
  }

  // Seed initial particles spread across screen height
  for (let i = 0; i < 60; i++) {
    const p = makeParticle();
    p.y = Math.random() * canvas.height;
    particles.push(p);
  }

  function tick() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    particles.forEach(p => {
      p.y       -= p.speed;
      p.x       += p.drift;
      p.opacity -= 0.0008;
      if (p.y < -10 || p.opacity <= 0) Object.assign(p, makeParticle());

      ctx.save();
      ctx.globalAlpha = p.opacity;
      ctx.fillStyle   = `hsl(${p.hue}, 80%, 70%)`;
      ctx.beginPath();
      ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    });
    requestAnimationFrame(tick);
  }
  tick();
})();

// ── Stick generation ─────────────────────────────────────────────
const STICK_COUNT = 10;
// Heights in px — tall enough to poke 40-60px above the vessel rim (174px from scene bottom).
// Sticks-layer bottom is at 38px, so a 180px stick reaches 38+180=218px (44px above rim).
const STICK_HEIGHTS = [155, 172, 185, 195, 188, 168, 158, 178, 163, 182];

function buildSticks(container) {
  container.innerHTML = '';
  STICK_HEIGHTS.forEach(h => {
    const s = document.createElement('div');
    s.className = 'stick';
    s.style.height = h + 'px';
    container.appendChild(s);
  });
}

buildSticks(sticksLanding);
buildSticks(sticksShake);

// ── Ambient vessel float on landing ─────────────────────────────
gsap.to('#tubeScene', {
  y: -6, duration: 2.2,
  ease: 'sine.inOut', yoyo: true, repeat: -1,
});

// ── Page transition ──────────────────────────────────────────────
function gotoShake() {
  sfx.click.play();
  gsap.to(pageLanding, { opacity: 0, duration: 0.4, onComplete: () => {
    pageLanding.classList.remove('active');
    pageShake.classList.add('active');
    gsap.fromTo(pageShake, { opacity: 0 }, { opacity: 1, duration: 0.5 });
    state.phase = 'shaking';
    startVesselIdle();
    tryEnableMotion();
  }});
}

btnStart.addEventListener('click', gotoShake);

// ── Idle vessel sway in shake phase ─────────────────────────────
let idleTween;
function startVesselIdle() {
  idleTween = gsap.to(vessel, {
    rotation: 2.5, duration: 1.2,
    ease: 'sine.inOut', yoyo: true, repeat: -1,
    transformOrigin: 'bottom center',
  });
}

// ── Shake button (desktop fallback + iOS permission trigger) ─────
btnShake.addEventListener('click', async () => {
  sfx.click.play();
  if (state.phase !== 'shaking' || state.drawn) return;

  // Request iOS motion permission on user gesture
  if (!state.motionEnabled) {
    await tryEnableMotion(true);
    shakeHint.textContent = '📳 现在摇动手机！';
    return;
  }

  // Desktop: inject shake energy
  addShakeEnergy(40);
});

// ── DeviceMotion ─────────────────────────────────────────────────
async function tryEnableMotion(fromGesture = false) {
  if (typeof DeviceMotionEvent === 'undefined') return;

  if (typeof DeviceMotionEvent.requestPermission === 'function') {
    if (!fromGesture) return; // Need user gesture for iOS
    try {
      const perm = await DeviceMotionEvent.requestPermission();
      if (perm === 'granted') enableMotion();
    } catch { /* denied or error */ }
  } else {
    enableMotion();
  }
}

function enableMotion() {
  state.motionEnabled = true;
  shakeHint.textContent = '📳 摇动手机求签！';

  window.addEventListener('devicemotion', e => {
    if (state.phase !== 'shaking' || state.drawn) return;
    const a = e.accelerationIncludingGravity || e.acceleration;
    if (!a) return;
    const mag = Math.sqrt((a.x || 0) ** 2 + (a.y || 0) ** 2 + (a.z || 0) ** 2);
    if (mag > 12) addShakeEnergy(mag * 1.5);
  });
}

// ── Shake energy accumulation ────────────────────────────────────
let shakeAnimFrame;
function addShakeEnergy(amount) {
  if (state.drawn) return;

  state.shakeAccum = Math.min(100, state.shakeAccum + amount);
  shakeBar.style.width = state.shakeAccum + '%';

  // Wobble vessel proportional to energy
  const angle = Math.min(state.shakeAccum / 10, 8);
  gsap.to(vessel, {
    rotation: (Math.random() > 0.5 ? 1 : -1) * angle,
    duration: 0.1, overwrite: true,
    transformOrigin: 'bottom center',
  });

  // Bounce sticks
  document.querySelectorAll('#sticksShake .stick').forEach(s => {
    gsap.to(s, {
      y: -(Math.random() * 6 + 2),
      duration: 0.12, yoyo: true, repeat: 1,
      ease: 'power2.out',
    });
  });

  if (!sfx.shake._playing) sfx.shake.play();

  clearTimeout(shakeAnimFrame);
  shakeAnimFrame = setTimeout(() => {
    // Decay energy when no shake input
    if (state.shakeAccum < 100) {
      state.shakeAccum = Math.max(0, state.shakeAccum - 5);
      shakeBar.style.width = state.shakeAccum + '%';
    }
  }, 400);

  if (state.shakeAccum >= 100) triggerDraw();
}

// ── Draw trigger ─────────────────────────────────────────────────
function triggerDraw() {
  if (state.drawn) return;
  state.drawn = true;
  state.phase = 'flying';

  sfx.shake.stop();
  sfx.crack.play();
  if (navigator.vibrate) navigator.vibrate([80, 40, 120]);

  idleTween && idleTween.kill();
  shakeBar.style.width = '100%';
  shakeBar.style.boxShadow = '0 0 16px rgba(240,208,96,1)';

  // Big shake burst
  const tl = gsap.timeline({ onComplete: () => flyStickOut() });
  tl.to(vessel, { rotation: -18, duration: 0.1, transformOrigin: 'bottom center' })
    .to(vessel, { rotation: 16,  duration: 0.12 })
    .to(vessel, { rotation: -12, duration: 0.1  })
    .to(vessel, { rotation: 9,   duration: 0.1  })
    .to(vessel, { rotation: -5,  duration: 0.1  })
    .to(vessel, { rotation: 0,   duration: 0.4, ease: 'elastic.out(1, 0.4)' });
}

// ── Stick fly animation + API call ───────────────────────────────
function flyStickOut() {
  sfx.reveal.play();

  const vesselRect = vessel.getBoundingClientRect();
  const wrapRect   = document.getElementById('gameWrap').getBoundingClientRect();
  const startX = vesselRect.left + vesselRect.width  / 2 - 5;
  const startY = vesselRect.top  - 20;
  const endX   = wrapRect.left + wrapRect.width  / 2 - 5;
  const endY   = wrapRect.top  + wrapRect.height / 2 - 75;

  gsap.set(flyingStick, { display: 'block', x: startX, y: startY, rotation: 0, opacity: 1, scale: 1 });

  const tl = gsap.timeline({
    onComplete: () => {
      gsap.to(flyingStick, { opacity: 0, duration: 0.4, onComplete: () => {
        flyingStick.style.display = 'none';
      }});
      showResult();
    }
  });

  tl.to(flyingStick, {
      x: endX, y: endY, rotation: 540,
      duration: 0.9, ease: 'power3.out',
    })
    .to(flyingStick, { scale: 1.3, duration: 0.15, ease: 'power2.out' }, '-=0.1')
    .to(flyingStick, { scale: 1.0, duration: 0.2 });

  // Fire API in parallel
  fetchLot().then(data => {
    window._lotData = data;
  }).catch(() => {
    window._lotData = null;
  });
}

// ── API fetch ────────────────────────────────────────────────────
async function fetchLot() {
  const body = new FormData();
  body.append('_', Date.now()); // cache-bust
  const res  = await fetch('api/draw-lot.php', { method: 'POST', body });
  const data = await res.json();
  if (!data.success) throw new Error('API error');
  return data;
}

// ── Show result popup ────────────────────────────────────────────
async function showResult() {
  // Wait for API data (max 3s)
  const start = Date.now();
  while (!window._lotData && Date.now() - start < 3000) {
    await new Promise(r => setTimeout(r, 100));
  }

  const data = window._lotData || {
    lot_no: '?', level: '中签', title: '平安顺遂',
    poem: '平湖秋月映天心\n安居乐业福满门\n顺应天时人和美\n遂心所愿步从容',
    summary: '诸事平顺，健康平安，静待时机。',
    redirect_url: '#',
  };

  document.getElementById('lotNo').textContent    = data.lot_no;
  document.getElementById('lotLevel').textContent = data.level;
  document.getElementById('lotTitle').textContent = data.title;
  document.getElementById('lotPoem').textContent  = data.poem;
  document.getElementById('lotSummary').textContent = data.summary;

  // Level color
  const levelEl = document.getElementById('lotLevel');
  if (data.level.includes('上上')) levelEl.style.color = '#C0392B';
  else if (data.level.includes('上')) levelEl.style.color = '#A0522D';
  else if (data.level.includes('下')) levelEl.style.color = '#666';

  btnCta.onclick = () => window.open(data.redirect_url, '_blank');

  // Animate popup in
  resultOverlay.style.display = 'flex';
  gsap.fromTo(resultOverlay, { opacity: 0 }, { opacity: 1, duration: 0.35 });
  gsap.fromTo(resultCard,
    { scale: 0.82, y: 20, opacity: 0 },
    { scale: 1,    y: 0,  opacity: 1, duration: 0.55, ease: 'back.out(1.6)', delay: 0.1 }
  );

  state.phase = 'result';
}
