// SimpleClock — a dummy-proof 24-hour day disc.
//
// Design intent:
//   • One ring, not two — the picker shows destination time.
//   • Sleep period is a dark navy wedge so it's instantly readable as "night".
//   • Each activity is a labeled ICON+TIME placed on the ring at its hour,
//     so users learn the meaning from the label, not a legend.
//   • A glowing pointer shows "now" + a center readout with the day's date,
//     destination time, and one sentence telling the user what they should
//     be doing right now (the same "do this now" data shown in the side card).

const { useState: useStateClk, useEffect: useEffectClk, useMemo: useMemoClk } = React;
const { useTween, hmToMin, fmtTime24, fmtTime12, tzLocalParts } = window.JL;

const TAU = Math.PI * 2;

// Convert HH:MM minute value to angle radians (0 = top, clockwise).
function minToAngle(min) { return (min / 1440) * TAU; }

// Polar → Cartesian helper.
function polar(cx, cy, r, ang) {
  return { x: cx + Math.sin(ang) * r, y: cy - Math.cos(ang) * r };
}

// Build an arc-wedge path that goes from inner-rim r1 to outer-rim r2,
// spanning angles fromRad → toRad. Used for sleep/day backgrounds.
function wedgePath(cx, cy, r1, r2, fromRad, toRad) {
  let d = toRad - fromRad;
  if (d < 0) d += TAU;
  const large = d > Math.PI ? 1 : 0;
  const a = polar(cx, cy, r2, fromRad);
  const b = polar(cx, cy, r2, toRad);
  const c = polar(cx, cy, r1, toRad);
  const dPt = polar(cx, cy, r1, fromRad);
  return [
    `M ${a.x.toFixed(2)} ${a.y.toFixed(2)}`,
    `A ${r2} ${r2} 0 ${large} 1 ${b.x.toFixed(2)} ${b.y.toFixed(2)}`,
    `L ${c.x.toFixed(2)} ${c.y.toFixed(2)}`,
    `A ${r1} ${r1} 0 ${large} 0 ${dPt.x.toFixed(2)} ${dPt.y.toFixed(2)}`,
    `Z`,
  ].join(' ');
}

// Render an event marker — colored dot with emoji + multi-line label.
// `radialOffset` lets us push the label further out when neighbors are close.
// If `onDrag` is provided, the dot becomes draggable; pointer events are
// converted to angle/minute via the SVG's getScreenCTM().
function EventMarker({ cx, cy, r, ang, icon, label, time, color, radialOffset = 28,
                      onDrag, svgRef, type }) {
  const p = polar(cx, cy, r, ang);
  const lp = polar(cx, cy, r + radialOffset, ang);
  const right = Math.sin(ang) > 0.08;
  const left  = Math.sin(ang) < -0.08;
  const anchor = right ? 'start' : left ? 'end' : 'middle';
  const dx = right ? 5 : left ? -5 : 0;
  const [dragging, setDragging] = React.useState(false);

  // Convert a pointer event's clientX/Y into the SVG's local viewBox coords
  // and from there into a minute-of-day value (0..1439).
  const pointerToMin = (ev) => {
    const svg = svgRef && svgRef.current;
    if (!svg) return null;
    const pt = svg.createSVGPoint();
    pt.x = ev.clientX; pt.y = ev.clientY;
    const ctm = svg.getScreenCTM();
    if (!ctm) return null;
    const loc = pt.matrixTransform(ctm.inverse());
    // angle from north (0 = top), clockwise
    let a = Math.atan2(loc.x - cx, -(loc.y - cy));
    if (a < 0) a += TAU;
    const min = Math.round((a / TAU) * 1440 / 5) * 5; // snap to 5 min
    return ((min % 1440) + 1440) % 1440;
  };

  const onPointerDown = (ev) => {
    if (!onDrag) return;
    ev.preventDefault();
    ev.stopPropagation();
    setDragging(true);
    const move = (ev2) => {
      const min = pointerToMin(ev2);
      if (min != null) onDrag(min);
    };
    const up = () => {
      setDragging(false);
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
      document.body.style.cursor = '';
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
    document.body.style.cursor = 'grabbing';
  };

  const dotR = onDrag ? 7.5 : 6.5;
  const fillR = onDrag ? 6 : 5;
  const cursor = onDrag ? (dragging ? 'grabbing' : 'grab') : 'default';

  return (
    <g style={{ animation: dragging ? 'none' : 'jlPop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both' }}>
      {/* leader line */}
      <line x1={p.x} y1={p.y} x2={lp.x} y2={lp.y}
        stroke="rgba(255,255,255,0.20)" strokeWidth="0.7" />
      {/* invisible larger hit area for easy grabbing */}
      {onDrag && (
        <circle cx={p.x} cy={p.y} r="14" fill="transparent"
          style={{ cursor }}
          onPointerDown={onPointerDown} />
      )}
      {/* dot */}
      <circle cx={p.x} cy={p.y} r={dotR} fill="#0a0a14"
        style={{ pointerEvents: 'none' }} />
      <circle cx={p.x} cy={p.y} r={fillR} fill={color}
        style={{
          filter: `drop-shadow(0 0 ${dragging ? 8 : 4}px ${color}${dragging ? 'cc' : '88'})`,
          pointerEvents: 'none',
          transition: dragging ? 'none' : 'filter .15s',
        }} />
      <text x={p.x} y={p.y + 2.3} fontSize="6.4" textAnchor="middle"
        style={{ userSelect: 'none', pointerEvents: 'none' }}>
        {icon}
      </text>
      {/* label */}
      <text x={lp.x + dx} y={lp.y - 2} fontSize="10.5" fill="#fafafa"
        textAnchor={anchor} fontFamily="'Inter', system-ui, sans-serif"
        fontWeight="700" letterSpacing="0.3"
        style={{ pointerEvents: 'none' }}>
        {label}
      </text>
      <text x={lp.x + dx} y={lp.y + 10} fontSize="9.5" fill={color}
        textAnchor={anchor} fontFamily="'JetBrains Mono', monospace" fontWeight="600"
        letterSpacing="0.5"
        style={{ pointerEvents: 'none' }}>
        {time}
      </text>
    </g>
  );
}

// Resolve label collisions: actually compute each label's approximate pixel
// position and push it radially out until it clears all previously placed
// labels. This catches the case where two labels share a nearly-identical
// angle and need real vertical separation, not just an angular nudge.
function resolveLabels(events, ringR = 165) {
  const sorted = events.map((e, i) => ({ ...e, _idx: i }))
    .sort((a, b) => a.ang - b.ang);

  const BASE = 28;
  const STEP = 22;          // px per radial bump
  const MAX_STEPS = 8;
  const MIN_Y_GAP = 24;     // labels need at least this much vertical space
  const X_RANGE = 90;       // ignore labels >this many px away horizontally

  sorted.forEach((e, i) => {
    let offset = 0;
    let ok = false;
    for (let s = 0; s < MAX_STEPS && !ok; s++) {
      offset = s * STEP;
      const r = ringR + BASE + offset;
      const yp = -Math.cos(e.ang) * r;
      const xp =  Math.sin(e.ang) * r;
      ok = true;
      // Check against every previously-placed label in this batch
      for (let j = 0; j < i; j++) {
        const prev = sorted[j];
        const rPrev = ringR + BASE + prev._offset;
        const yPrev = -Math.cos(prev.ang) * rPrev;
        const xPrev =  Math.sin(prev.ang) * rPrev;
        if (Math.abs(xp - xPrev) < X_RANGE && Math.abs(yp - yPrev) < MIN_Y_GAP) {
          ok = false;
          break;
        }
      }
    }
    e._offset = offset;
    e._radial = BASE + offset;
  });

  const out = events.slice();
  sorted.forEach((e) => { out[e._idx]._radial = e._radial; });
  return out;
}

// Compute what the user should be doing right now (mirrors advice.jsx logic
// but returns short label + icon for in-dial display).
function nowDoing(day, destTz) {
  const local = tzLocalParts(destTz, new Date());
  const nowMin = local.hour * 60 + local.minute;
  const inWindow = (s, e) => {
    const a = hmToMin(s), b = hmToMin(e);
    if (a <= b) return nowMin >= a && nowMin <= b;
    return nowMin >= a || nowMin <= b;
  };
  if (inWindow(day.sleep.start, day.sleep.end))
    return { icon: '💤', text: 'Sleep' };
  const minToBed = ((hmToMin(day.sleep.start) - nowMin + 1440) % 1440);
  if (minToBed < 90) return { icon: '🌙', text: 'Wind down' };
  if (day.light && nowMin >= hmToMin(day.light.start) && nowMin <= hmToMin(day.light.end))
    return { icon: '☀️', text: 'Bright light' };
  for (const [k, t] of Object.entries(day.meals)) {
    if (Math.abs(hmToMin(t) - nowMin) < 30)
      return { icon: k === 'breakfast' ? '🍳' : k === 'lunch' ? '🥗' : '🍽️', text: `${k[0].toUpperCase()}${k.slice(1)}` };
  }
  if (day.caffeine && nowMin > hmToMin(day.caffeine.end))
    return { icon: '🚫', text: 'No caffeine' };
  return { icon: '✨', text: 'Stay active' };
}

function DualClock({ plan, selectedIdx = null, onDragMarker }) {
  const computedIdx = useMemoClk(() => {
    if (!plan) return 0;
    if (selectedIdx != null) return selectedIdx;
    const now = Date.now();
    for (let i = 0; i < plan.days.length; i++) {
      const d = plan.days[i].date.getTime();
      if (now >= d && now < d + 86400000) return i;
    }
    return 0;
  }, [plan, selectedIdx]);

  const day = plan && plan.days[computedIdx];

  const [now, setNow] = useStateClk(() => new Date());
  useEffectClk(() => {
    const id = setInterval(() => setNow(new Date()), 30000);
    return () => clearInterval(id);
  }, []);
  const svgRef = React.useRef(null);

  if (!plan || !day) return null;

  // Geometry — generous size so labels fit
  const W = 460, H = 460, cx = 230, cy = 230;
  const Ro = 165;          // outer ring radius (where dots sit)
  const Ri = 130;          // inner ring radius
  const SLEEP_OPACITY = 0.5;

  // "Now" in destination wall-clock time
  const destLocal = tzLocalParts(plan.destTz, now);
  const destMin = destLocal.hour * 60 + destLocal.minute;
  const destAngle = minToAngle(destMin);
  const tweenDest = useTween(destAngle, 800);

  // Build list of events to render around the ring.
  // Two consolidations to reduce label clutter at near-identical times:
  //   1. If bright-light starts at wake-up, merge into one marker.
  //   2. Defer/merge breakfast if it falls within 25 min of the wake marker.
  const sameTime = (a, b) => Math.abs(hmToMin(a) - hmToMin(b)) < 6;

  const wakeAndLight = sameTime(day.sleep.end, day.light.start);

  const events = [
    wakeAndLight ? {
      type: 'wake',
      ang: minToAngle(hmToMin(day.sleep.end)),
      icon: '🌅', label: 'Wake + light',
      time: fmtTime24(day.sleep.end.h, day.sleep.end.m), color: '#ffd166',
    } : {
      type: 'wake',
      ang: minToAngle(hmToMin(day.sleep.end)),
      icon: '🌅', label: 'Wake up',
      time: fmtTime24(day.sleep.end.h, day.sleep.end.m), color: '#ffd166',
    },
    !wakeAndLight && {
      type: null,
      ang: minToAngle(hmToMin(day.light.start)),
      icon: '☀️', label: 'Bright light',
      time: fmtTime24(day.light.start.h, day.light.start.m), color: '#ffd166',
    },
    { type: 'breakfast', ang: minToAngle(hmToMin(day.meals.breakfast)),
      icon: '🍳', label: 'Breakfast', time: fmtTime24(day.meals.breakfast.h, day.meals.breakfast.m), color: '#ffd87a' },
    { type: 'lunch', ang: minToAngle(hmToMin(day.meals.lunch)),
      icon: '🥗', label: 'Lunch', time: fmtTime24(day.meals.lunch.h, day.meals.lunch.m), color: '#ff9b50' },
    day.caffeine ? { type: 'caffeine-cutoff', ang: minToAngle(hmToMin(day.caffeine.end)),
      icon: '🚫', label: 'No coffee after', time: fmtTime24(day.caffeine.end.h, day.caffeine.end.m), color: '#c86e3c' } : null,
    { type: 'dinner', ang: minToAngle(hmToMin(day.meals.dinner)),
      icon: '🍽️', label: 'Dinner', time: fmtTime24(day.meals.dinner.h, day.meals.dinner.m), color: '#a86d4d' },
    day.melatonin ? { type: 'melatonin', ang: minToAngle(hmToMin(day.melatonin.time)),
      icon: '💊', label: 'Melatonin', time: fmtTime24(day.melatonin.time.h, day.melatonin.time.m), color: '#a866ff' } : null,
    { type: 'bed', ang: minToAngle(hmToMin(day.sleep.start)),
      icon: '💤', label: 'Bedtime', time: fmtTime24(day.sleep.start.h, day.sleep.start.m), color: '#5a78ff' },
  ].filter(Boolean);

  // Doing-right-now (compact for center)
  const doing = nowDoing(day, plan.destTz);

  // Run collision resolution to stagger labels for crowded sections.
  // Pass the outer ring radius so it can compute true pixel positions.
  const resolvedEvents = resolveLabels(events, Ro);

  // Sleep wedge spans bedtime → wake; may wrap midnight
  const sleepStart = minToAngle(hmToMin(day.sleep.start));
  const sleepEnd   = minToAngle(hmToMin(day.sleep.end));

  return (
    <div style={{ width: '100%', display: 'flex', justifyContent: 'center' }}>
      <svg ref={svgRef} viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', maxWidth: 520, height: 'auto', overflow: 'visible' }}>
        <defs>
          <radialGradient id="dayBg" cx="50%" cy="50%">
            <stop offset="0%" stopColor="rgba(80, 100, 200, 0.10)" />
            <stop offset="80%" stopColor="rgba(80, 100, 200, 0.03)" />
            <stop offset="100%" stopColor="transparent" />
          </radialGradient>
          <linearGradient id="dayArc" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="rgba(255, 209, 102, 0.20)" />
            <stop offset="100%" stopColor="rgba(255, 209, 102, 0.05)" />
          </linearGradient>
          <linearGradient id="sleepArc" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="rgba(60, 76, 160, 0.45)" />
            <stop offset="100%" stopColor="rgba(30, 30, 80, 0.75)" />
          </linearGradient>
        </defs>

        {/* Background disc — soft glow */}
        <circle cx={cx} cy={cy} r={Ri - 8} fill="url(#dayBg)" />

        {/* Daytime arc (light fill, full ring background) */}
        <circle cx={cx} cy={cy} r={(Ro + Ri) / 2} fill="none"
          stroke="url(#dayArc)" strokeWidth={Ro - Ri} opacity="0.9" />

        {/* Sleep wedge — dark navy block clearly indicating "night" */}
        <path d={wedgePath(cx, cy, Ri, Ro, sleepStart, sleepEnd)}
          fill="url(#sleepArc)" opacity={SLEEP_OPACITY} />

        {/* Hour ticks (every hour, taller every 3, labels every 6) */}
        {[...Array(24)].map((_, h) => {
          const ang = (h / 24) * TAU;
          const big = h % 6 === 0;
          const med = h % 3 === 0;
          const p1 = polar(cx, cy, Ri - 2, ang);
          const p2 = polar(cx, cy, Ri - (big ? 12 : med ? 8 : 4), ang);
          const lp = polar(cx, cy, Ri - 22, ang);
          return (
            <g key={h}>
              <line x1={p1.x} y1={p1.y} x2={p2.x} y2={p2.y}
                stroke={big ? 'rgba(255,255,255,0.6)' : 'rgba(255,255,255,0.25)'}
                strokeWidth={big ? 1.4 : med ? 0.7 : 0.4} />
              {big && (
                <text x={lp.x} y={lp.y + 3} fontSize="11"
                  fill="rgba(255,255,255,0.6)" textAnchor="middle"
                  fontFamily="monospace" fontWeight="500">
                  {String(h).padStart(2, '0')}
                </text>
              )}
            </g>
          );
        })}

        {/* Sleep ribbon labels at start/end so user reads "11:00 PM → 7:00 AM" */}
        <g opacity="0.85">
          {(() => {
            const mid = ((hmToMin(day.sleep.start) + (hmToMin(day.sleep.end) - hmToMin(day.sleep.start) + 1440) % 1440 / 2) % 1440);
            const midAng = minToAngle(mid);
            const lp = polar(cx, cy, (Ri + Ro) / 2, midAng);
            return (
              <g>
                <text x={lp.x} y={lp.y - 3} fontSize="10" fill="rgba(255,255,255,0.85)"
                  textAnchor="middle" fontFamily="'Inter', system-ui" fontWeight="700"
                  letterSpacing="2">SLEEP</text>
                <text x={lp.x} y={lp.y + 9} fontSize="9" fill="rgba(255,255,255,0.6)"
                  textAnchor="middle" fontFamily="monospace">
                  {fmtTime24(day.sleep.start.h, day.sleep.start.m)} – {fmtTime24(day.sleep.end.h, day.sleep.end.m)}
                </text>
              </g>
            );
          })()}
        </g>

        {/* Event markers — icons + time labels around the outer ring */}
        {resolvedEvents.map((e, i) => (
          <EventMarker key={i} cx={cx} cy={cy} r={Ro}
            ang={e.ang} icon={e.icon} label={e.label} time={e.time} color={e.color}
            radialOffset={e._radial}
            type={e.type}
            svgRef={svgRef}
            onDrag={e.type && onDragMarker ? (min) => onDragMarker(e.type, min) : null} />
        ))}

        {/* "Now" pointer — bright arrow from inside ring pointing to the
            current hour on the outer ring */}
        <g>
          <line
            x1={polar(cx, cy, 60, tweenDest).x} y1={polar(cx, cy, 60, tweenDest).y}
            x2={polar(cx, cy, Ro + 8, tweenDest).x} y2={polar(cx, cy, Ro + 8, tweenDest).y}
            stroke="#fff" strokeWidth="2.4" strokeLinecap="round"
            style={{ filter: 'drop-shadow(0 0 6px rgba(255,255,255,0.65))' }} />
          {/* arrowhead */}
          <circle cx={polar(cx, cy, Ro + 8, tweenDest).x} cy={polar(cx, cy, Ro + 8, tweenDest).y} r="5" fill="#fff"
            style={{ filter: 'drop-shadow(0 0 8px rgba(255,255,255,0.8))' }} />
          {/* hub */}
          <circle cx={cx} cy={cy} r="4" fill="#fff" />
        </g>

        {/* "NOW" label following the pointer outside the ring — sits at the
            outermost ring of labels (offset 7 × STEP) so event labels never
            overlap it. */}
        {(() => {
          const lp = polar(cx, cy, Ro + 28 + 7 * 22, tweenDest);
          const right = Math.sin(tweenDest) > 0.1;
          const left = Math.sin(tweenDest) < -0.1;
          const anchor = right ? 'start' : left ? 'end' : 'middle';
          return (
            <g>
              <text x={lp.x} y={lp.y} fontSize="11" fill="#fff" textAnchor={anchor}
                fontFamily="'Inter', system-ui" fontWeight="800" letterSpacing="2"
                style={{ filter: 'drop-shadow(0 0 6px rgba(255,255,255,0.6))' }}>
                ★ NOW
              </text>
            </g>
          );
        })()}

        {/* Center readout — date, big time, current action */}
        <g>
          <text x={cx} y={cy - 30} fontSize="9" fill="rgba(255,255,255,0.55)"
            textAnchor="middle" letterSpacing="3" fontFamily="monospace" fontWeight="600">
            {day.date.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }).toUpperCase()}
          </text>
          <text x={cx} y={cy + 6} fontSize="42" fill="#fff"
            textAnchor="middle" fontFamily="'Inter', system-ui, sans-serif"
            fontWeight="200" letterSpacing="-1"
            style={{ fontVariantNumeric: 'tabular-nums' }}
            key={`tn-${destMin}`} className="jl-digit">
            {fmtTime24(destLocal.hour, destLocal.minute)}
          </text>
          <text x={cx} y={cy + 22} fontSize="9" fill="rgba(255,255,255,0.5)"
            textAnchor="middle" letterSpacing="2" fontFamily="monospace">
            DESTINATION TIME
          </text>
          {/* Doing-now chip below */}
          <g key={`now-${doing.text}`} className="jl-pop"
            transform={`translate(${cx} ${cy + 46})`}>
            <rect x={-60} y={-12} width="120" height="24" rx="12"
              fill="rgba(255,255,255,0.08)" stroke="rgba(255,255,255,0.18)" strokeWidth="0.6" />
            <text x={-46} y={5} fontSize="13" textAnchor="start">{doing.icon}</text>
            <text x={-26} y={5} fontSize="11" fill="#fff" fontFamily="'Inter', system-ui"
              fontWeight="600" textAnchor="start">
              {doing.text}
            </text>
          </g>
        </g>
      </svg>
    </div>
  );
}

window.DualClock = DualClock;
