// Break Debate booking flow walkthrough animation.
// 26s; 1920×1080; live HTML; damped cursor; cross-fades.

const TOTAL = 26;

// Discrete event times (seconds) — drives state machine.
const T = {
  // Scene 1: Dashboard (steps 1)
  cursorIdleStart: 0,
  cursorToLDTile: 0.4,
  clickLDTile: 1.7,
  // Scene 2: Course page (step 2)
  coursePageIn: 1.85,
  cursorToBookSession: 2.2,
  clickBookSession: 3.6,
  // Scene 3: Booking modal opens
  bookingModalIn: 3.75,
  cursorToCoachField: 4.1,
  clickCoachField: 5.2,
  // Scene 4: Coach dropdown opens, scroll, select Archan (step 3-4)
  coachDropdownOpen: 5.3,
  cursorToArchanScroll: 5.6,
  clickArchan: 7.0,
  // Scene 5: Duration dropdown (step 5)
  cursorToDurationField: 7.25,
  clickDurationField: 8.2,
  durationDropdownOpen: 8.25,
  cursorToSixty: 8.55,
  clickSixty: 9.3,
  // Scene 6: Pick May 16 (step 6)
  cursorToMay16: 9.55,
  clickMay16: 10.6,
  // Scene 7: Pick 7 AM (step 7)
  cursorTo7am: 10.85,
  click7am: 11.9,
  // Scene 8: Confirm
  cursorToConfirm: 12.15,
  clickConfirm: 13.1,
  confirmModalIn: 13.25,
  // Scene 9: Confirm Booking (after dwell)
  cursorToConfirmBooking: 15.6,
  clickConfirmBooking: 16.55,
  // Scene 10: Back to dashboard / course page with session
  coursePageReturnIn: 16.75,
  cursorToSessionCard: 17.1,
  clickSessionCard: 18.55,
  // Scene 11: Session detail
  sessionDetailIn: 18.75,
  end: TOTAL,
};

// Step through `T` returns the current discrete state of the booking flow.
function bookingStateAt(t) {
  const s = {
    coachName: 'Jerry Chen',
    duration: '45 mins',
    coachDropdownOpen: false,
    durationDropdownOpen: false,
    coachListScroll: 0,
    selectedDay: null,
    selectedTime: null,
    hoveredTime: null,
    selectedDateLabel: 'Friday, May 15',
    confirmActive: false,
    highlightArchan: false,
    highlightSixty: false,
    timeSlotsScroll: 0,
  };

  if (t >= T.coachDropdownOpen && t < T.clickArchan) {
    s.coachDropdownOpen = true;
    // Scroll: from 0 at coachDropdownOpen → 0.9 at cursorToArchanScroll-ish
    const scrollStart = T.coachDropdownOpen;
    const scrollEnd = T.cursorToArchanScroll + 0.6;
    if (t < scrollStart) s.coachListScroll = 0;
    else if (t > scrollEnd) s.coachListScroll = 0.9;
    else {
      const lt = (t - scrollStart) / (scrollEnd - scrollStart);
      s.coachListScroll = Easing.easeInOutCubic(lt) * 0.9;
    }
    if (t >= T.cursorToArchanScroll + 0.5) {
      s.highlightArchan = true;
    }
  }
  if (t >= T.clickArchan) {
    s.coachName = 'Archan Sen';
  }
  if (t >= T.durationDropdownOpen && t < T.clickSixty) {
    s.durationDropdownOpen = true;
    if (t >= T.cursorToSixty + 0.4) {
      s.highlightSixty = true;
    }
  }
  if (t >= T.clickSixty) {
    s.duration = '60 mins';
  }
  if (t >= T.clickMay16) {
    s.selectedDay = 16;
    s.selectedDateLabel = 'Saturday, May 16';
    // Scroll slots down to show 7 AM
    s.timeSlotsScroll = 0.95;
  }
  // Build a tiny lead-in scroll while cursor is approaching 7am
  if (t >= T.clickMay16 && t < T.cursorTo7am) {
    const lt = (t - T.clickMay16) / (T.cursorTo7am - T.clickMay16);
    s.timeSlotsScroll = 0.4 + Easing.easeInOutCubic(lt) * 0.55;
  } else if (t >= T.cursorTo7am) {
    s.timeSlotsScroll = 0.95;
  }
  if (t >= T.cursorTo7am + 0.5 && t < T.click7am) {
    s.hoveredTime = '07:00 AM';
  }
  if (t >= T.click7am) {
    s.selectedTime = '07:00 AM';
    s.confirmActive = true;
  }
  return s;
}

// --- Anchors (canvas-local coordinates, hand-tuned for 1920x1080) -----------
// Cursor waypoints reference these names; positions verified visually in iterate.
const A = {
  IDLE: [1100, 540],

  // Dashboard
  LD_TILE: [780, 703],

  // Course page
  BOOK_BTN: [820, 727],
  SESSION_CARD: [560, 330],

  // Booking modal
  COACH_FIELD: [555, 268],
  ARCHAN_ITEM: [678, 531],   // dropdown row for "Archan Sen"
  DURATION_FIELD: [555, 336],
  SIXTY_ITEM: [678, 448],   // duration dropdown row for "60 mins"
  MAY_16: [1254, 368],   // calendar day cell; verified from CalendarGrid layout
  SEVEN_AM: [1465, 392],     // after scroll, 7 AM lands at ~y=392
  CONFIRM_BTN: [1584, 881],

  // Confirm modal (centered at ~1072, 519; button group near bottom)
  CONFIRM_BOOKING_BTN: [1197, 685],
};

// Build cursor waypoints from the timing constants.
function buildWaypoints() {
  return [
    { time: T.cursorIdleStart, ...xy(A.IDLE) },
    { time: T.cursorToLDTile, ...xy(A.IDLE) },                          // hold
    { time: T.clickLDTile, ...xy(A.LD_TILE), click: true },

    // After click + transition, cursor stays at LD_TILE momentarily then moves.
    { time: T.cursorToBookSession, ...xy(A.LD_TILE) },
    { time: T.clickBookSession, ...xy(A.BOOK_BTN), click: true },

    { time: T.cursorToCoachField, ...xy(A.BOOK_BTN) },
    { time: T.clickCoachField, ...xy(A.COACH_FIELD), click: true },

    { time: T.cursorToArchanScroll, ...xy(A.COACH_FIELD) },
    { time: T.clickArchan, ...xy(A.ARCHAN_ITEM), click: true },

    { time: T.cursorToDurationField, ...xy(A.ARCHAN_ITEM) },
    { time: T.clickDurationField, ...xy(A.DURATION_FIELD), click: true },

    { time: T.cursorToSixty, ...xy(A.DURATION_FIELD) },
    { time: T.clickSixty, ...xy(A.SIXTY_ITEM), click: true },

    { time: T.cursorToMay16, ...xy(A.SIXTY_ITEM) },
    { time: T.clickMay16, ...xy(A.MAY_16), click: true },

    { time: T.cursorTo7am, ...xy(A.MAY_16) },
    { time: T.click7am, ...xy(A.SEVEN_AM), click: true },

    { time: T.cursorToConfirm, ...xy(A.SEVEN_AM) },
    { time: T.clickConfirm, ...xy(A.CONFIRM_BTN), click: true },

    { time: T.cursorToConfirmBooking, ...xy(A.CONFIRM_BTN) },
    { time: T.clickConfirmBooking, ...xy(A.CONFIRM_BOOKING_BTN), click: true },

    { time: T.cursorToSessionCard, ...xy(A.CONFIRM_BOOKING_BTN) },
    { time: T.clickSessionCard, ...xy(A.SESSION_CARD), click: true },

    { time: T.end, ...xy(A.SESSION_CARD) },
  ];
}
const xy = ([x, y]) => ({ x, y });

// --- Layered scenes ---------------------------------------------------------

function App() {
  return (
    <Stage width={1920} height={1080} duration={TOTAL} background="#fff" persistKey="bd-walkthrough">
      <Scene />
    </Stage>
  );
}

function Scene() {
  const t = useTime();

  // Update data-screen-label every second for comment context.
  const labelRef = React.useRef(null);
  React.useEffect(() => {
    if (labelRef.current) {
      labelRef.current.setAttribute('data-screen-label', `${Math.floor(t)}s`);
    }
  }, [Math.floor(t)]);

  const state = bookingStateAt(t);
  const waypoints = React.useMemo(buildWaypoints, []);

  // Refs (currently unused — anchors are hardcoded for stability)
  const refs = {
    coachField: React.useRef(null),
    durationField: React.useRef(null),
    archanItem: React.useRef(null),
    sixtyItem: React.useRef(null),
    may16: React.useRef(null),
    sevenAm: React.useRef(null),
    confirmBtn: React.useRef(null),
    confirmBookingBtn: React.useRef(null),
  };

  // Scene opacities (cross-fades between layers).
  // Stagger: outgoing scene fades out fast, then incoming fades in — minimal overlap.
  const dashOp = sceneOpacity(t, -0.5, 0.3, T.clickLDTile, 0.22);
  const courseOp = sceneOpacity(t, T.coursePageIn, 0.35, T.clickBookSession, 0.22);

  // Booking modal lifetime
  const bookingOp = sceneOpacity(t, T.bookingModalIn, 0.4, T.clickConfirm, 0.28);

  // Confirm modal lifetime
  const confirmOp = sceneOpacity(t, T.confirmModalIn - 0.15, 0.35, T.clickConfirmBooking, 0.25);
  const confirmRise = riseOffset(t, T.confirmModalIn - 0.15, 0.5);

  // After confirm: course page returns (with upcoming session)
  const courseReturnOp = sceneOpacity(t, T.coursePageReturnIn - 0.15, 0.45, T.clickSessionCard, 0.3);

  // Final session detail modal
  const sessionDetailOp = sceneOpacity(t, T.sessionDetailIn - 0.15, 0.4, T.end + 1, 0.3);
  const sessionDetailRise = riseOffset(t, T.sessionDetailIn - 0.15, 0.6);

  // Modal scrim — darken behind floating modals.
  const modalScrim = Math.max(bookingOp, confirmOp, sessionDetailOp);

  // Transition scrim — soft dim during scene swaps, per brief.
  const transScrim = transitionScrim(t);
  const combinedScrim = Math.max(modalScrim, transScrim);

  return (
    <div ref={labelRef} data-screen-label="0s" style={{
      position: 'absolute', inset: 0,
      background: '#f6f7f9',
      display: 'flex',
      fontFamily: BD.font,
    }}>
      <Sidebar activeNav="My Courses" />

      {/* MAIN STAGE — stacked layers */}
      <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
        {/* Layer: Dashboard (step 1) */}
        <Layer opacity={dashOp}>
          <Dashboard showUpcomingSession={false} />
        </Layer>

        {/* Layer: Course page (no sessions) */}
        <Layer opacity={courseOp}>
          <CoursePage showUpcomingSession={false} />
        </Layer>

        {/* Layer: Course page (with upcoming session) — after booking */}
        <Layer opacity={courseReturnOp}>
          <CoursePage
            showUpcomingSession={true}
            sessionCardClicked={t >= T.clickSessionCard - 0.05}
          />
        </Layer>

        {/* Scrim */}
        {combinedScrim > 0.01 && (
          <div style={{
            position: 'absolute', inset: 0,
            background: BD.scrim,
            opacity: combinedScrim,
            pointerEvents: 'none',
          }}/>
        )}

        {/* Booking modal */}
        <Layer opacity={bookingOp}>
          <BookingModal state={state} refs={refs} />
        </Layer>

        {/* Confirm modal — centered, rises in */}
        {confirmOp > 0.01 && (
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            opacity: confirmOp,
            transform: `translateY(${confirmRise}px)`,
            pointerEvents: 'none',
          }}>
            <ConfirmModal refs={refs} />
          </div>
        )}

        {/* Session detail modal */}
        {sessionDetailOp > 0.01 && (
          <div style={{
            position: 'absolute', inset: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            opacity: sessionDetailOp,
            transform: `translateY(${sessionDetailRise}px)`,
            pointerEvents: 'none',
          }}>
            <SessionDetailModal />
          </div>
        )}
      </div>

      {/* Cursor — always on top, in absolute canvas coords */}
      <Cursor waypoints={waypoints} />
    </div>
  );
}

function Layer({ opacity, children }) {
  if (opacity <= 0.01) return null;
  return (
    <div style={{
      position: 'absolute', inset: 0,
      opacity,
      pointerEvents: 'none',
      display: 'flex', flexDirection: 'column',
    }}>
      {children}
    </div>
  );
}

// Cross-fade helpers
function sceneOpacity(t, fadeInStart, fadeInDur, fadeOutStart, fadeOutDur) {
  if (t < fadeInStart) return 0;
  if (t < fadeInStart + fadeInDur) {
    const lt = (t - fadeInStart) / fadeInDur;
    return Easing.easeOutCubic(lt);
  }
  if (t < fadeOutStart) return 1;
  if (t < fadeOutStart + fadeOutDur) {
    const lt = (t - fadeOutStart) / fadeOutDur;
    return 1 - Easing.easeInCubic(lt);
  }
  return 0;
}

// Brief flash of dim during scene transitions — peaks at the click moment, decays quickly.
function transitionScrim(t) {
  // Each scene change: a soft dim that peaks at the transition midpoint.
  const transitions = [
    { center: T.clickLDTile + 0.1, dur: 0.45, peak: 0.45 },
    { center: T.clickConfirmBooking + 0.1, dur: 0.45, peak: 0.45 },
  ];
  let max = 0;
  for (const tr of transitions) {
    const half = tr.dur / 2;
    const dist = Math.abs(t - tr.center);
    if (dist < half) {
      const lt = 1 - (dist / half);
      max = Math.max(max, tr.peak * Easing.easeOutCubic(lt));
    }
  }
  return max;
}

// Rise-in: returns offset px that goes from 8px to 0 over the entry window.
function riseOffset(t, start, dur) {
  if (t < start) return 8;
  if (t > start + dur) return 0;
  const lt = (t - start) / dur;
  return 8 * (1 - Easing.easeOutCubic(lt));
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
