/* global React, Button */
// ============================================================================
// The session card (027) — an exercise as Pelle's gym card shows it: how-to with pictures
// and video, last time's sets, today's weight, and set rows to fill in and tick.
//
// A plan_item with rx renders as an ExerciseCard in Today instead of a Done/Skip row.
// Today's weight = the heaviest set last time, + rx.inc when every set reached the top of
// the range, never past rx.cap (the block's ceiling). Ticking a set saves it to set_log and
// starts the rest timer; once rx.sets sets are ticked the plan item is marked done.
// ============================================================================
const { useState: useStateK, useEffect: useEffectK } = React;

const numK = (v) => { if (v == null || v === '') return null; const n = Number(String(v).replace(',', '.')); return isFinite(n) ? n : null; };
const fmtKgK = (x) => x == null ? '' : String(Math.round(x * 10) / 10);
const shortDateK = (s) => new Date(s + 'T12:00:00').toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });
const UNIT_K = { reps: 'reps', s: 'seconds', m: 'metres' };

function rxText(rx) {
  const n = rx.lo === rx.hi ? rx.hi : `${rx.lo}–${rx.hi}`;
  return `${rx.sets}×${n}${rx.unit === 's' ? ' s' : rx.unit === 'm' ? ' m' : ''}${rx.side ? ' /side' : ''}`;
}

// Today's target kg, and why, from the last session that logged this exercise
function targetK(rx, last) {
  const done = last ? last.sets.filter(s => s.done) : [];
  const allTop = done.length >= rx.sets && done.every(s => numK(s.reps) != null && numK(s.reps) >= rx.hi);
  if (rx.noload) return { kg: null, hint: allTop && rx.inc_text ? `top of the range last time: ${rx.inc_text}` : '' };
  const kgs = done.map(s => numK(s.kg)).filter(x => x != null);
  let kg = kgs.length ? Math.max(...kgs) : (rx.start ?? null), hint = '';
  if (kgs.length && allTop && rx.inc) { kg += rx.inc; hint = `top of the range last time: +${fmtKgK(rx.inc)} kg`; }
  if (rx.cap != null && kg != null && kg > rx.cap) { kg = rx.cap; hint = `${fmtKgK(rx.cap)} kg is the top for this block`; }
  return { kg, hint };
}

function setsTextK(rx, sets) {
  const done = sets.filter(s => s.done);
  const reps = done.map(s => fmtKgK(numK(s.reps))).join(', ');
  const unit = rx.unit === 's' ? ' s' : rx.unit === 'm' ? ' m' : '';
  const kgs = [...new Set(done.map(s => numK(s.kg)).filter(x => x != null))];
  if (rx.noload || kgs.length === 0) return reps + unit;
  if (kgs.length === 1) return `${kgs[0] === 0 ? 'bodyweight' : fmtKgK(kgs[0]) + ' kg'} × ${reps}${unit}`;
  return done.map(s => `${fmtKgK(numK(s.kg))}×${fmtKgK(numK(s.reps))}`).join(', ') + unit;
}

// ---------------------------------------------------------------- rest timer (one per page)
const RestTimer = (() => {
  let end = null, subs = new Set(), tick = null, audio = null, wake = null;
  const left = () => end == null ? null : Math.max(0, Math.round((end - Date.now()) / 1000));
  const emit = () => subs.forEach(f => f(left()));
  function beepAt(sec) {
    try {   // scheduled inside the tap, so iOS lets it play even if the page is throttled meanwhile
      audio = audio || new (window.AudioContext || window.webkitAudioContext)();
      const o = audio.createOscillator(), g = audio.createGain(), t = audio.currentTime + sec;
      o.frequency.value = 880; g.gain.setValueAtTime(0.0001, t); g.gain.exponentialRampToValueAtTime(0.3, t + 0.02);
      g.gain.exponentialRampToValueAtTime(0.0001, t + 0.4); o.connect(g).connect(audio.destination); o.start(t); o.stop(t + 0.45);
    } catch (e) { /* no audio: the countdown still shows */ }
  }
  return {
    start(sec) {
      end = Date.now() + sec * 1000; beepAt(sec);
      try { if (navigator.wakeLock && !wake) navigator.wakeLock.request('screen').then(l => { wake = l; l.addEventListener('release', () => { wake = null; }); }).catch(() => {}); } catch (e) {}
      clearInterval(tick);
      tick = setInterval(() => { emit(); if (left() === 0) { clearInterval(tick); tick = null; if (navigator.vibrate) navigator.vibrate(300); } }, 250);
      emit();
    },
    stop() { end = null; clearInterval(tick); tick = null; if (audio) { audio.close(); audio = null; } emit(); },
    subscribe(f) { subs.add(f); f(left()); return () => subs.delete(f); },
  };
})();

function RestBar() {
  const [left, setLeft] = useStateK(null);
  useEffectK(() => RestTimer.subscribe(setLeft), []);
  const mmss = (s) => `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;
  const btn = { flex: 1, padding: '10px 0', border: '1px solid var(--border-strong)', borderRadius: 8, background: 'var(--bg-raised)',
                fontFamily: 'var(--font-mono)', fontSize: 15, cursor: 'pointer', color: 'var(--fg)' };
  return (
    <div style={{ position: 'fixed', left: 0, right: 0, bottom: 'calc(54px + env(safe-area-inset-bottom, 0px))', zIndex: 29,
                  background: 'rgba(252,252,253,0.97)', backdropFilter: 'blur(10px)', borderTop: '1px solid var(--border)' }}>
      <div style={{ maxWidth: 720, margin: '0 auto', display: 'flex', gap: 8, alignItems: 'center', padding: '8px 12px' }}>
        <button onClick={() => RestTimer.stop()} aria-label="Stop the rest timer" style={{ all: 'unset', cursor: 'pointer', minWidth: 64,
          fontSize: 22, fontWeight: 600, fontFamily: left != null ? 'var(--font-mono)' : 'var(--font-sans)',
          color: left === 0 ? 'var(--success)' : 'var(--fg)' }}>
          {left == null ? 'Rest' : left === 0 ? 'Go' : mmss(left)}
        </button>
        {[60, 90, 120, 180].map(s => <button key={s} style={btn} onClick={() => RestTimer.start(s)}>{mmss(s)}</button>)}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------- the card
function HowTo({ rx, name }) {
  const q = encodeURIComponent(rx.q || name);
  const a = { color: 'var(--signal)', textDecoration: 'none', marginRight: 16 };
  return (
    <details style={{ marginTop: 8 }}>
      <summary style={{ cursor: 'pointer', color: 'var(--signal)', fontSize: 13 }}>How to · pictures · video</summary>
      {rx.how && rx.how.length > 0 && <ol style={{ margin: '8px 0 6px', paddingLeft: 20, fontSize: 13, color: 'var(--fg)' }}>
        {rx.how.map((s, i) => <li key={i} style={{ marginBottom: 3 }}>{s}</li>)}
      </ol>}
      <div style={{ fontSize: 13 }}>
        <a style={a} href={`https://www.google.com/search?tbm=isch&q=${q}`} target="_blank" rel="noopener">Pictures ↗</a>
        <a style={a} href={`https://www.youtube.com/results?search_query=${q}`} target="_blank" rel="noopener">Video ↗</a>
      </div>
    </details>
  );
}

function ExerciseCard({ item, memberDb, day, today, last, canWrite, blocked, onChanged }) {
  const rx = item.rx;
  const T = targetK(rx, last);
  const [rows, setRows] = useStateK(() => {
    const r = Array.from({ length: rx.sets }, () => ({ kg: '', reps: '', done: false }));
    (today || []).forEach(s => { r[s.set_no - 1] = { kg: s.kg == null ? '' : fmtKgK(Number(s.kg)), reps: s.reps == null ? '' : fmtKgK(Number(s.reps)), done: !!s.done }; });
    return r.map(x => x || { kg: '', reps: '', done: false });
  });
  const [err, setErr] = useStateK(null);
  const status = item.status;
  const closed = status === 'skipped' || status === 'changed';
  const live = canWrite && !blocked && !closed;
  const kgPh = rx.noload ? '—' : T.kg == null ? 'kg' : T.kg === 0 ? 'BW' : fmtKgK(T.kg);

  async function save(i, row) {
    try {
      await window.PlanAPI.sets.save(memberDb, { plan_item_id: item.id, ex_key: rx.key, day, set_no: i + 1,
        kg: rx.noload ? null : numK(row.kg), reps: numK(row.reps), done: row.done });
      setErr(null);
    } catch (e) { setErr('Not saved. Check the connection and tick again.'); }
  }
  const edit = (i, field, v) => setRows(rs => rs.map((r, j) => j === i ? { ...r, [field]: v } : r));
  const blur = (i) => { if (rows[i].done) save(i, rows[i]); };

  async function toggle(i) {
    const r = rows[i], nowDone = !r.done;
    const row = { ...r, done: nowDone };
    if (nowDone) {
      if (!rx.noload && row.kg === '' && T.kg != null) row.kg = fmtKgK(T.kg);
      if (row.reps === '') row.reps = String(rx.hi);
    }
    const next = rows.map((x, j) => j === i ? row : x);
    setRows(next);
    if (nowDone && rx.rest) RestTimer.start(rx.rest);
    await save(i, row);
    const n = next.filter(x => x.done).length;
    const top = Math.max(0, ...next.filter(x => x.done).map(x => numK(x.kg) || 0));
    try {
      if (n >= rx.sets && status !== 'done') onChanged(await window.PlanAPI.logItem(item.id, 'done', rx.noload ? null : top));
      else if (n < rx.sets && status === 'done') onChanged(await window.PlanAPI.logItem(item.id, 'released'));
    } catch (e) { setErr('Sets saved, but the exercise could not be marked done.'); }
  }
  async function skip(to) {
    try { onChanged(await window.PlanAPI.logItem(item.id, to)); } catch (e) { setErr('Could not save: ' + e.message); }
  }

  const input = { width: '100%', boxSizing: 'border-box', padding: '10px 8px', border: '1px solid var(--border-strong)', borderRadius: 6,
                  fontSize: 16, fontFamily: 'var(--font-mono)', background: 'var(--bg-raised)', color: 'var(--fg)' };
  const grid = { display: 'grid', gridTemplateColumns: '32px 1fr 1fr 48px', gap: 8, alignItems: 'center' };
  const doneN = rows.filter(r => r.done).length;

  return (
    <div style={{ borderTop: '1px solid var(--border)', margin: '0 -12px', padding: '12px 12px 14px',
                  background: status === 'done' ? 'var(--success-tint)' : closed ? 'var(--ink-25)' : 'transparent' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 12 }}>
        <div style={{ width: 44, fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-muted)' }}>{String(item.start_time || '').slice(0, 5)}</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'baseline' }}>
            <div style={{ fontSize: 16, fontWeight: 600, textDecoration: status === 'skipped' ? 'line-through' : 'none' }}>{item.title}</div>
            <div style={{ fontSize: 15, fontWeight: 600, fontFamily: 'var(--font-mono)', whiteSpace: 'nowrap' }}>{rxText(rx)}</div>
          </div>
          <div style={{ fontSize: 13, color: 'var(--fg-muted)', marginTop: 2 }}>
            {rx.rest ? `Rest ${rx.rest} s` : ''}{rx.inc ? ` · progress by ${fmtKgK(rx.inc)} kg` : rx.inc_text ? ` · progress: ${rx.inc_text}` : ''}
            {item.ladder ? <span style={{ color: 'var(--signal)' }}> · {item.ladder.name}</span> : null}
          </div>
          {item.cue && <div style={{ fontSize: 14, marginTop: 6 }}>{item.cue}</div>}
          {item.detail && <div style={{ fontSize: 13, color: 'var(--fg-muted)', marginTop: 4 }}>{item.detail}</div>}
          <HowTo rx={rx} name={item.title} />

          <div style={{ fontSize: 13, color: 'var(--fg-muted)', margin: '10px 0 8px' }}>
            {last ? <>Last {shortDateK(last.day)}: <b style={{ color: 'var(--fg)', fontFamily: 'var(--font-mono)' }}>{setsTextK(rx, last.sets)}</b>
                      {T.hint && <span style={{ color: 'var(--signal)' }}> · {T.hint}</span>}</>
                  : rx.noload ? 'First time on the log.' : 'First time on the log. Start lighter than you think.'}
          </div>

          {status === 'changed' && item.rule_note && <div style={{ fontSize: 13, color: 'var(--warning)', marginBottom: 6 }}>{item.rule_note}</div>}
          <div style={{ ...grid, fontSize: 11, color: 'var(--fg-subtle)', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>
            <span>Set</span><span>{rx.noload ? '' : rx.per_hand ? 'kg / hand' : 'kg'}</span><span>{UNIT_K[rx.unit || 'reps']}</span><span>Done</span>
          </div>
          {rows.map((r, i) => (
            <div key={i} style={{ ...grid, marginBottom: 6 }}>
              <span style={{ fontFamily: 'var(--font-mono)', fontSize: 15, textAlign: 'center' }}>{i + 1}</span>
              <input inputMode="decimal" aria-label={`${item.title} set ${i + 1} kg`} placeholder={kgPh} value={r.kg} disabled={!live || rx.noload}
                     onChange={e => edit(i, 'kg', e.target.value)} onBlur={() => blur(i)} style={{ ...input, opacity: rx.noload ? 0.4 : 1 }} />
              <input inputMode="decimal" aria-label={`${item.title} set ${i + 1} ${UNIT_K[rx.unit || 'reps']}`} placeholder={String(rx.hi)} value={r.reps}
                     disabled={!live} onChange={e => edit(i, 'reps', e.target.value)} onBlur={() => blur(i)} style={input} />
              <button aria-label={`Set ${i + 1} done`} aria-pressed={r.done} disabled={!live} onClick={() => toggle(i)} style={{
                height: 42, borderRadius: 6, border: '1px solid ' + (r.done ? 'var(--success)' : 'var(--border-strong)'), cursor: live ? 'pointer' : 'default',
                background: r.done ? 'var(--success)' : 'var(--bg-raised)', color: '#fff', fontSize: 18 }}>{r.done ? '✓' : ''}</button>
            </div>
          ))}
          {err && <div style={{ fontSize: 13, color: 'var(--danger, #B42318)', marginTop: 4 }}>{err}</div>}
          {live && (
            <div style={{ display: 'flex', gap: 12, alignItems: 'center', marginTop: 6 }}>
              <button onClick={() => setRows(rs => [...rs, { kg: '', reps: '', done: false }])} style={{ all: 'unset', cursor: 'pointer', fontSize: 13,
                padding: '6px 10px', border: '1px dashed var(--border-strong)', borderRadius: 6, color: 'var(--fg-muted)' }}>+ Add set</button>
              <span style={{ flex: 1, fontSize: 12, color: 'var(--fg-subtle)', textAlign: 'right' }}>{doneN} of {rx.sets} sets</span>
              {doneN === 0 && <Button kind="ghost" size="sm" onClick={() => skip('skipped')}>Skip</Button>}
            </div>
          )}
          {canWrite && blocked && !closed && <div style={{ marginTop: 6 }}><Button kind="secondary" size="sm" onClick={() => skip('skipped')}>Skip (it hurts)</Button></div>}
          {canWrite && status === 'skipped' && <button onClick={() => skip('released')} style={{ all: 'unset', cursor: 'pointer', fontSize: 13, color: 'var(--signal)', marginTop: 6 }}>Undo skip</button>}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ExerciseCard, RestBar, RestTimer });
