/* global React, MiniDayDial, Meter, SH_EL, AndroidStatus */
// Skyhearth mobile — PORT / RAISE (the "Focus Dial"), wired to the SHARED CORE.
// The Aetheric Wheel is the input surface: tap a SLICE or its ICON to select a
// vein; PRESS-AND-HOLD to train it (the ring fills, then dispatches TRAIN). The
// reducer owns ALL rules — main+leak, on-time, slot advance, appetite, ascension,
// collapse, death. This screen only reads state and dispatches.

const { useState: useS, useRef: useR, useEffect: useE } = React;
const CW = window.SkyhearthCore;
const { RING, HOME_SLOT, SLOTS, C, ritualReady, ringNeighbor } = CW;

const HOLD_MS = 720;
const SLOT_ICONS = ['☾', '☀', '◐', '☽'];
const slotHomeEls = (slotIdx) => RING.filter(id => HOME_SLOT[id] === SLOTS[slotIdx].id);

// ---------- interactive radial wheel ----------
function InteractiveWheel({ size = 300, elements, selected, slotIdx, onTrainStart, holdEl, holdProgress }) {
  const cx = size / 2, cy = size / 2;
  const ringR = size * 0.385;
  const secOuter = size * 0.475, secInner = size * 0.205;
  const moteSize = size * 0.235, glow = moteSize * 2.1;
  const ontime = slotHomeEls(slotIdx);

  function pt(r, deg) { const a = (deg - 90) * Math.PI / 180; return [cx + r * Math.cos(a), cy + r * Math.sin(a)]; }
  function wedge(centerDeg, half, ro, ri) {
    const a0 = centerDeg - half, a1 = centerDeg + half;
    const [ox0, oy0] = pt(ro, a0), [ox1, oy1] = pt(ro, a1);
    const [ix1, iy1] = pt(ri, a1), [ix0, iy0] = pt(ri, a0);
    return `M ${ox0} ${oy0} A ${ro} ${ro} 0 0 1 ${ox1} ${oy1} L ${ix1} ${iy1} A ${ri} ${ri} 0 0 0 ${ix0} ${iy0} Z`;
  }
  const nodes = RING.map((id, i) => { const deg = i * 45; const [x, y] = pt(ringR, deg); return { id, i, deg, x, y }; });

  // on-time arc between the two home elements of the active slot
  const homeIdx = ontime.map(h => RING.indexOf(h)).filter(i => i >= 0).sort((a, b) => a - b);
  let arc = null;
  if (homeIdx.length === 2) {
    const [p0x, p0y] = pt(ringR, homeIdx[0] * 45), [p1x, p1y] = pt(ringR, homeIdx[1] * 45);
    arc = `M ${p0x} ${p0y} A ${ringR} ${ringR} 0 0 1 ${p1x} ${p1y}`;
  }

  return (
    <div style={{ width: size, height: size, position: 'relative', touchAction: 'none' }}>
      <svg viewBox={`0 0 ${size} ${size}`} style={{ width: size, height: size, position: 'absolute', inset: 0 }}>
        {nodes.map(n => {
          const el = SH_EL[n.id]; const unlocked = elements[n.id].unlocked;
          const isSel = selected === n.id;
          return (
            <path key={n.id} d={wedge(n.deg, 21.5, secOuter, secInner)}
              fill={unlocked ? el.color : 'var(--ink)'}
              fillOpacity={isSel ? 0.18 : unlocked ? 0.06 : 0.02}
              stroke={isSel ? el.color : 'var(--ink-ghost)'} strokeWidth={isSel ? 1.4 : 0.6}
              strokeDasharray={unlocked ? 'none' : '3 3'}
              style={{ cursor: unlocked ? 'pointer' : 'not-allowed', transition: 'fill-opacity 160ms ease, stroke 160ms ease' }}
              onPointerDown={(e) => { e.preventDefault(); onTrainStart(n.id); }} />
          );
        })}
        <circle cx={cx} cy={cy} r={ringR} fill="none" stroke="var(--ink-ghost)" strokeWidth="1" strokeDasharray="2 4" />
        {nodes.map(n => <line key={n.id} x1={cx} y1={cy} x2={n.x} y2={n.y} stroke="var(--ink-trace)" strokeWidth="0.7" strokeDasharray="2 3" />)}
        {arc && <path d={arc} fill="none" stroke="var(--stamp-amber)" strokeWidth="3.5" strokeLinecap="round" strokeOpacity="0.7" />}
      </svg>

      <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', display: 'flex', flexDirection: 'column', alignItems: 'center', pointerEvents: 'none' }}>
        <div style={{ position: 'absolute', top: '42%', left: '50%', transform: 'translate(-50%,-50%)', width: glow, height: glow, borderRadius: '50%', background: 'radial-gradient(circle, rgba(120,170,210,0.4), transparent 70%)' }} />
        <img className="drop-white" src="assets/drift-mote.png" alt="" style={{ width: moteSize, height: moteSize, display: 'block' }} />
        <div className="mname" style={{ marginTop: 0 }}>Drift-Mote</div>
      </div>

      {nodes.map(n => {
        const el = SH_EL[n.id]; const unlocked = elements[n.id].unlocked;
        const isSel = selected === n.id; const isOn = ontime.includes(n.id);
        const holding = holdEl === n.id;
        return (
          <div key={n.id} className={'wheel-node' + (unlocked ? '' : ' sealed')}
            style={{ position: 'absolute', left: n.x, top: n.y, transform: 'translate(-50%,-50%)', color: unlocked ? el.color : 'var(--ink-ghost)', cursor: unlocked ? 'pointer' : 'not-allowed', touchAction: 'none' }}
            onPointerDown={(e) => { e.preventDefault(); onTrainStart(n.id); }}>
            <div className="chip" style={{ position: 'relative', background: 'var(--paper-light)', boxShadow: isSel ? `0 0 0 3px ${el.color}` : isOn && unlocked ? '0 0 0 3px rgba(184,99,42,0.25)' : 'none', transition: 'box-shadow 160ms ease' }}>
              {unlocked ? <img className="drop-white" src={el.icon} alt="" /> : <span style={{ fontSize: 14 }}>{el.glyph}</span>}
              {holding && (
                <svg style={{ position: 'absolute', inset: -4, pointerEvents: 'none' }} width="46" height="46" viewBox="0 0 46 46">
                  <circle cx="23" cy="23" r="21" fill="none" stroke="rgba(26,46,74,0.12)" strokeWidth="3" />
                  <circle cx="23" cy="23" r="21" fill="none" stroke="var(--stamp-amber)" strokeWidth="3" strokeLinecap="round"
                    strokeDasharray={2 * Math.PI * 21} strokeDashoffset={2 * Math.PI * 21 * (1 - holdProgress)} transform="rotate(-90 23 23)" />
                </svg>
              )}
            </div>
            {unlocked
              ? <><span className="wn">{el.name}</span><span className="wv" style={{ color: isSel ? el.color : 'var(--ink-faded)' }}>{elements[n.id].mastery}</span></>
              : <span className="wn">Sealed</span>}
          </div>
        );
      })}
    </div>
  );
}

// ---------- a compact condition meter cell ----------
function CondCell({ name, value, max = 100, tone }) {
  return (
    <div className="col grow g4" style={{ minWidth: 0 }}>
      <div className="row between"><span className="label" style={{ fontSize: 7.5 }}>{name}</span><span className="mono" style={{ fontSize: 9.5 }}>{Math.round(value)}</span></div>
      <Meter value={value} max={max} segs={7} tone={tone} />
    </div>
  );
}

// ---------- a tend action chip ----------
function TendChip({ label, sub, onTap, disabled, accent }) {
  return (
    <button onPointerDown={disabled ? undefined : onTap} disabled={disabled}
      style={{ flex: 1, minWidth: 0, textAlign: 'center', border: `1.5px solid ${accent || 'var(--ink-faded)'}`,
        background: 'rgba(244,236,214,0.5)', color: 'var(--ink)', padding: '7px 4px 6px', borderRadius: 2,
        opacity: disabled ? 0.4 : 1, cursor: disabled ? 'default' : 'pointer', display: 'flex', flexDirection: 'column', gap: 1 }}>
      <span className="fell" style={{ fontSize: 12.5, lineHeight: 1 }}>{label}</span>
      <span className="mono" style={{ fontSize: 7.5, color: 'var(--ink-faded)', letterSpacing: 0 }}>{sub}</span>
    </button>
  );
}

// ---------- the live Port screen ----------
function PortFocusLive({ state, dispatch, onLaunch, popupOpen, tabBar }) {
  const [selected, setSelected] = useS('fire');
  const [holdEl, setHoldEl] = useS(null);
  const [holdProg, setHoldProg] = useS(0);
  const [floaties, setFloaties] = useS([]);
  const [shake, setShake] = useS(false);
  const hold = useR({ raf: 0, timer: 0, t0: 0, id: null, fired: false });

  const { elements, slotIdx, day, daySchedule, care, hull, rank, formName, manifestDays, manifestMax, onTimeUsedToday, town, resources } = state;
  const RANK_NAMES = CW.RANK_NAMES;

  function commitTrain(id) {
    if (!elements[id].unlocked) return;
    // compute the leak preview BEFORE dispatch (for the floaty)
    const onTime = HOME_SLOT[id] === SLOTS[slotIdx].id && !onTimeUsedToday.includes(id);
    dispatch({ type: 'TRAIN', main: id });
    const fid = Math.random();
    setFloaties(f => [...f, { id: fid, el: id, txt: `+${C.MAIN} ${SH_EL[id].name}` }]);
    setTimeout(() => setFloaties(f => f.filter(x => x.id !== fid)), 950);
  }
  function startHold(id) {
    setSelected(id);
    if (!elements[id].unlocked) { setShake(true); setTimeout(() => setShake(false), 360); return; }
    if (popupOpen) return;
    hold.current = { raf: 0, timer: 0, t0: performance.now(), id, fired: false };
    setHoldEl(id); setHoldProg(0);
    const finish = () => { const h = hold.current; if (h.id === id && !h.fired) { h.fired = true; commitTrain(id); endHold(); } };
    hold.current.timer = setTimeout(finish, HOLD_MS);
    const loop = () => {
      const h = hold.current; if (h.id !== id || h.fired) return;
      const p = Math.min(1, (performance.now() - h.t0) / HOLD_MS);
      setHoldProg(p);
      if (p >= 1) finish(); else h.raf = requestAnimationFrame(loop);
    };
    hold.current.raf = requestAnimationFrame(loop);
  }
  function endHold() { cancelAnimationFrame(hold.current.raf); clearTimeout(hold.current.timer); hold.current.id = null; setHoldEl(null); setHoldProg(0); }
  useE(() => {
    const up = () => { if (hold.current.id && !hold.current.fired) endHold(); };
    window.addEventListener('pointerup', up); window.addEventListener('pointercancel', up);
    return () => { window.removeEventListener('pointerup', up); window.removeEventListener('pointercancel', up); };
  }, []);

  const el = SH_EL[selected];
  const selUnlocked = elements[selected].unlocked;
  const selVal = elements[selected].mastery;
  const onTimeNow = HOME_SLOT[selected] === SLOTS[slotIdx].id && !onTimeUsedToday.includes(selected);
  const nb = ringNeighbor(selected);
  const ready = ritualReady(state);
  const canLaunch = rank >= 1;

  // tend affordances (mirror reducer guards)
  const full = care.fullness;
  const dis = {
    feed: full >= 90 || resources.staple < 1,
    dine: town.currency < 4 || full > 90,
    forage: full > 90,
    rest: false, bond: false, attune: false, harness: hull >= C.HULL_MAX,
  };
  const tend = (kind, extra) => () => { if (popupOpen) return; dispatch({ type: 'CARE_ACTION', kind, ...(extra || {}) }); };

  return (
    <div className={'phone sh-m' + (shake ? ' shake' : '')}>
      <AndroidStatus />
      <div className="row between aic" style={{ padding: '4px 16px 0' }}>
        <div className="col">
          <span className="label">Now Raising</span>
          <span className="fell" style={{ fontSize: 18, lineHeight: 1 }}>{formName}</span>
          <span className="label" style={{ fontSize: 8, marginTop: 2 }}>{RANK_NAMES[rank]} · {manifestDays}/{manifestMax} day{manifestMax !== 1 ? 's' : ''} left</span>
        </div>
        <MiniDayDial day={day} slotIdx={slotIdx} size={50} schedule={daySchedule.map(a => a ? { color: a.color } : null)} />
      </div>

      {/* hero wheel */}
      <div className="grow col center aic" style={{ position: 'relative', paddingTop: 0 }}>
        <div style={{ position: 'relative' }}>
          <InteractiveWheel elements={elements} selected={selected} slotIdx={slotIdx}
            onTrainStart={startHold} holdEl={holdEl} holdProgress={holdProg} />
          {floaties.map(f => (
            <span key={f.id} className="floaty" style={{ left: '50%', top: 14, transform: 'translateX(-50%)', color: SH_EL[f.el].color, fontSize: 22 }}>{f.txt}</span>
          ))}
        </div>
        <span className="label" style={{ marginTop: -2, color: 'var(--stamp-amber)' }}>
          {SLOTS[slotIdx].label} · on-time {slotHomeEls(slotIdx).map(e => SH_EL[e].name).join(' & ')}
        </span>
      </div>

      {/* condition — full care model, 2 rows of 3 */}
      <div className="col g4" style={{ padding: '0 16px 6px' }}>
        <div className="row g8">
          <CondCell name="Fatigue" value={care.fatigue} tone={care.fatigue >= 80 ? 'crit' : 'warn'} />
          <CondCell name="Fullness" value={care.fullness} tone={care.fullness < 12 ? 'crit' : 'ok'} />
          <CondCell name="Bond" value={care.bondPct} tone="gold" />
        </div>
        <div className="row g8">
          <CondCell name="Weight" value={care.weight} max={20} tone={care.weight <= 6 ? 'crit' : 'on'} />
          <CondCell name="Will" value={care.will} tone="on" />
          <CondCell name="Hull" value={hull} max={C.HULL_MAX} tone={hull <= 6 ? 'crit' : 'on'} />
        </div>
      </div>

      {/* sheet */}
      <div className="sheet">
        <div className="handle" />
        <div className="slots" style={{ marginBottom: 9 }}>
          {SLOTS.map((s, i) => (
            <div key={s.id} className={'slot' + (i === slotIdx ? ' active' : daySchedule[i] ? ' done' : '')}>
              <span className="ic">{SLOT_ICONS[i]}</span><span>{s.label.slice(0, 3)}</span>
            </div>
          ))}
        </div>

        {/* selected vein detail */}
        <div className="row g10 aic" style={{ marginBottom: 9 }}>
          <div className="chip" style={{ width: 40, height: 40, flex: '0 0 40px', borderRadius: '50%', border: `1.5px solid ${selUnlocked ? el.color : 'var(--ink-ghost)'}`, color: selUnlocked ? el.color : 'var(--ink-ghost)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            {selUnlocked ? <img className="drop-white" src={el.icon} alt="" style={{ width: 26, height: 26 }} /> : <span>{el.glyph}</span>}
          </div>
          <div className="col grow">
            <span className="fell" style={{ fontSize: 15 }}>{el.name}{selUnlocked ? <span className="mono muted" style={{ fontSize: 11 }}> · {selVal}</span> : ''}</span>
            {selUnlocked
              ? <span className="mono muted" style={{ fontSize: 10 }}>hold to train · <b style={{ color: 'var(--ink)' }}>+{C.MAIN}</b>{nb && elements[nb].unlocked ? <> · leak <span style={onTimeNow ? { color: 'var(--stamp-amber)', fontWeight: 600 } : {}}>+{onTimeNow ? C.LEAK_ON : C.LEAK_OFF}</span> → {SH_EL[nb].name}</> : <> · leak sealed</>}</span>
              : <span className="mono muted" style={{ fontSize: 10 }}>sealed vein · rescue in the field to unseal</span>}
          </div>
          {onTimeNow && selUnlocked && <span className="stamp stamp-amber" style={{ fontSize: 8 }}>On-time</span>}
        </div>

        {/* ascension cue */}
        {ready && ready.ok && (
          <button onPointerDown={tend('ritual')} style={{ width: '100%', border: '1.5px solid var(--ok)', background: 'rgba(77,110,63,0.10)', color: 'var(--ok)', borderRadius: 2, padding: '8px 10px', marginBottom: 9, display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer' }}>
            <span className="fell" style={{ fontSize: 13 }}>✦ Ascension ready · {ready.formKind === 'blend' ? 'Blend' : ready.formKind === 'pure' ? 'Pure' : 'Lesser'} {SH_EL[ready.vein] ? SH_EL[ready.vein].name : ''}</span>
            <span className="mono" style={{ fontSize: 9.5 }}>Perform rite ▸</span>
          </button>
        )}
        {ready && !ready.ok && !ready.metaLocked && (
          <div className="mono" style={{ fontSize: 8.5, color: 'var(--ink-faded)', marginBottom: 8, textAlign: 'center', letterSpacing: '0.04em' }}>next form: {ready.why}</div>
        )}

        {/* tend grid — full set */}
        <div className="col g6" style={{ marginBottom: 9 }}>
          <div className="row g6">
            <TendChip label="Feed" sub={`staple · ${resources.staple}`} disabled={dis.feed} onTap={tend('feed', { food: 'staple' })} />
            <TendChip label="Dine" sub="4 coin" disabled={dis.dine} onTap={tend('dine')} />
            <TendChip label="Forage" sub="Nature" disabled={dis.forage} onTap={tend('forage')} />
            <TendChip label="Rest" sub="−fatigue" disabled={dis.rest} onTap={tend('rest')} />
          </div>
          <div className="row g6">
            <TendChip label="Bond" sub="+12 trust" disabled={dis.bond} onTap={tend('bond')} />
            <TendChip label="Attune" sub="+10 will" disabled={dis.attune} onTap={tend('attune')} />
            <TendChip label="Harness" sub="+5 hull" disabled={dis.harness} onTap={tend('harness')} />
            <div style={{ flex: 1 }} />
          </div>
        </div>

        <button className="btn-primary" style={{ fontSize: 15, padding: 11, opacity: canLaunch ? 1 : 0.45 }} onPointerDown={canLaunch ? onLaunch : undefined}>
          {canLaunch ? 'Launch Expedition ▸' : 'Launch · Rank ≥ Lesser'}
        </button>
      </div>
      {tabBar}
    </div>
  );
}

Object.assign(window, { InteractiveWheel, PortFocusLive });
