/* Track & Throw — Views: Competitions list, detail, Stats, Settings */

// ---------- CompetitionsView ----------
function CompetitionsView({ data, onUpdate, onOpenComp, onAddComp }) {
  const seasons = useMemo(() => {
    const s = new Set();
    for (const c of data.competitions) { const y = seasonOf(c.date); if (y) s.add(y); }
    return [...s].sort((a,b) => b - a);
  }, [data.competitions]);

  const [seasonFilter, setSeasonFilter] = useState('all');

  // Reset filter if removed
  useEffect(() => {
    if (seasonFilter !== 'all' && !seasons.includes(seasonFilter)) {
      setSeasonFilter('all');
    }
  }, [seasons, seasonFilter]);

  const filtered = useMemo(() => {
    let l = [...data.competitions];
    if (seasonFilter !== 'all') l = l.filter(c => seasonOf(c.date) === seasonFilter);
    l.sort((a, b) => (b.date || '').localeCompare(a.date || ''));
    return l;
  }, [data.competitions, seasonFilter]);

  if (data.competitions.length === 0) {
    return <EmptyState onAdd={onAddComp} onLoadDemo={() => onUpdate({ ...data, competitions: demoSeed().competitions })}/>;
  }

  return (
    <Fragment>
      <SeasonFilter seasons={seasons} value={seasonFilter} onChange={setSeasonFilter} />
      <div className="section-label">Competitions · {filtered.length}</div>
      {filtered.map(c => <CompetitionRow key={c.id} comp={c} onClick={() => onOpenComp(c.id)}/>)}
    </Fragment>
  );
}

function EmptyState({ onAdd, onLoadDemo }) {
  return (
    <div className="empty">
      <div className="emoji-icon"><Icon.flag/></div>
      <h3>No competitions yet</h3>
      <p>Track every javelin (or shot put, discus, hammer) competition. Log each throw, foul or fair, and watch the season unfold.</p>
      <div className="flex gap-8" style={{justifyContent:'center'}}>
        <button className="btn" onClick={onAdd}><Icon.plus width="16" height="16"/> New competition</button>
        <button className="btn secondary" onClick={onLoadDemo}>Load demo data</button>
      </div>
    </div>
  );
}

function SeasonFilter({ seasons, value, onChange }) {
  if (!seasons.length) return null;
  return (
    <div className="season-switch">
      <button className={value === 'all' ? 'active' : ''} onClick={() => onChange('all')}>All</button>
      {seasons.map(y => (
        <button key={y} className={value === y ? 'active' : ''} onClick={() => onChange(y)}>{y}</button>
      ))}
    </div>
  );
}

function CompetitionRow({ comp, onClick }) {
  const best = bestThrow(comp.throws);
  const evt = EVENTS[comp.event] || EVENTS.javelin;
  return (
    <button className="row" onClick={onClick}>
      <div className="row-main">
        <div className="row-title">{comp.name || 'Untitled competition'}</div>
        <div className="row-sub">
          {comp.date && <span>{formatDate(comp.date, { day:'numeric', month:'short', year:'numeric' })}</span>}
          {comp.location && <span> <span className="dot-divider">·</span> {comp.location}</span>}
          <span> <span className="dot-divider">·</span> {evt.name}</span>
        </div>
        <div style={{marginTop:6, display:'flex', gap:6, flexWrap:'wrap'}}>
          {comp.ranking ? <span className={`chip rank rank-${Math.min(comp.ranking, 3)}`}>{ordinalize(comp.ranking)}</span> : null}
          <span className="chip">{countLegal(comp.throws)}/{countAttempts(comp.throws)} throws</span>
        </div>
      </div>
      <div className="row-end">
        <div className="pb-val">{best ? formatDistance(best.distance) : '—'}</div>
        <div className="pb-unit">{best ? 'm best' : 'no mark'}</div>
      </div>
    </button>
  );
}

// ---------- CompetitionDetail (full screen modal) ----------
function CompetitionDetail({ comp, onChange, onDelete }) {
  if (!comp) return null;
  const evt = EVENTS[comp.event] || EVENTS.javelin;
  const best = bestThrow(comp.throws);
  const legal = countLegal(comp.throws);
  const total = countAttempts(comp.throws);
  const { ask, ui: confirmUI } = useConfirm();

  const updateField = (k, v) => onChange({ ...comp, [k]: v });

  const updateThrow = (id, patch) => {
    onChange({ ...comp, throws: comp.throws.map(t => t.id === id ? { ...t, ...patch } : t) });
  };
  const removeThrow = (id) => onChange({ ...comp, throws: comp.throws.filter(t => t.id !== id) });
  const addThrow = () => onChange({ ...comp, throws: [...comp.throws, { id: uid('thr'), distance: null, foul: false, videoUrl: '' }] });

  const [editingThrow, setEditingThrow] = useState(null);

  const handleDelete = async () => {
    const yes = await ask({ title: 'Delete competition?', message: 'This cannot be undone.', confirmLabel: 'Delete', danger: true });
    if (yes) onDelete(comp.id);
  };

  return (
    <Fragment>
      {confirmUI}
      <div className="detail-grid">
        <div className="card" style={{padding:'14px 14px 12px'}}>
          <div className="field">
            <label>Competition name</label>
            <input className="input" value={comp.name} placeholder="e.g. Regional Championships" onChange={e => updateField('name', e.target.value)} />
          </div>
          <div className="spacer-12"></div>
          <div className="field-row">
            <div className="field">
              <label>Date</label>
              <input className="input" type="date" value={comp.date || ''} onChange={e => updateField('date', e.target.value)} />
            </div>
            <div className="field">
              <label>Event</label>
              <select className="select" value={comp.event} onChange={e => updateField('event', e.target.value)}>
                {Object.entries(EVENTS).map(([k, v]) => <option key={k} value={k}>{v.name}</option>)}
              </select>
            </div>
          </div>
          <div className="spacer-12"></div>
          <div className="field-row">
            <div className="field">
              <label>Location</label>
              <input className="input" value={comp.location || ''} placeholder="City" onChange={e => updateField('location', e.target.value)} />
            </div>
            <div className="field" style={{maxWidth:120}}>
              <label>Final position</label>
              <input
                className="input mono"
                type="number"
                inputMode="numeric"
                min="1"
                value={comp.ranking || ''}
                placeholder="—"
                onChange={e => {
                  const v = e.target.value === '' ? null : Math.max(1, parseInt(e.target.value, 10));
                  updateField('ranking', Number.isFinite(v) ? v : null);
                }}
              />
              {comp.ranking ? <div style={{fontSize:11, color:'var(--ink-3)', marginTop:4, fontFamily:'var(--mono)'}}>{ordinalize(comp.ranking)} place</div> : null}
            </div>
          </div>
        </div>

        <div className="card">
          <div className="throws-summary">
            <div className="big">{best ? formatDistance(best.distance) + 'm' : '—'}</div>
            <div className="small muted">best · {legal}/{total} successful</div>
          </div>
          <div className="throws-list">
            {comp.throws.map((t, idx) => (
              <ThrowRow
                key={t.id}
                num={idx + 1}
                t={t}
                isBest={best && best.id === t.id}
                onChange={(p) => updateThrow(t.id, p)}
                onRemove={async () => {
                  const yes = await ask({ title: 'Delete throw?', message: `Throw ${idx + 1} will be removed.`, confirmLabel: 'Delete', danger: true });
                  if (yes) removeThrow(t.id);
                }}
                onEditMore={() => setEditingThrow(t.id)}
              />
            ))}
          </div>
          <button className="btn secondary block" style={{marginTop:10}} onClick={addThrow}>
            <Icon.plus width="14" height="14"/> Add throw
          </button>
        </div>

        <div className="card">
          <div className="field">
            <label>Notes</label>
            <textarea className="textarea" value={comp.notes || ''} placeholder="Conditions, technique cues, sensations…" onChange={e => updateField('notes', e.target.value)} />
          </div>
        </div>

        <button className="btn ghost" style={{color:'#c0392b', marginTop:6}} onClick={handleDelete}>
          <Icon.trash width="15" height="15"/> Delete competition
        </button>
      </div>

      <Sheet open={!!editingThrow} onClose={() => setEditingThrow(null)} title={(() => {
        const idx = comp.throws.findIndex(t => t.id === editingThrow);
        return idx >= 0 ? `Throw ${idx + 1}` : 'Throw';
      })()}>
        {editingThrow && (() => {
          const t = comp.throws.find(t => t.id === editingThrow);
          if (!t) return null;
          return (
            <ThrowEditor t={t} onChange={(p) => updateThrow(t.id, p)} />
          );
        })()}
      </Sheet>
    </Fragment>
  );
}

// Single throw row in the throws list
function ThrowRow({ num, t, isBest, onChange, onRemove, onEditMore }) {
  const [val, setVal] = useState(t.distance == null ? '' : String(t.distance));
  useEffect(() => { setVal(t.distance == null ? '' : String(t.distance)); }, [t.distance]);

  const commit = (raw) => {
    const s = raw.replace(',', '.').trim();
    if (s === '') {
      onChange({ distance: null });
      return;
    }
    const v = parseFloat(s);
    if (Number.isFinite(v)) onChange({ distance: v });
  };

  const toggleFoul = () => onChange({ foul: !t.foul });

  return (
    <div className={'throw-row ' + (t.foul ? 'foul ' : '') + (isBest ? 'best' : '')}>
      <div className="num">{num}</div>
      <input
        className="dist-input"
        type="text"
        inputMode="decimal"
        placeholder={t.foul ? 'Foul' : '00.00'}
        value={val}
        onChange={e => setVal(e.target.value)}
        onBlur={() => commit(val)}
        onKeyDown={e => { if (e.key === 'Enter') e.target.blur(); }}
        disabled={t.foul}
        style={{color: t.foul ? 'var(--foul)' : undefined}}
      />
      <div className="right-controls">
        {t.videoUrl && (
          <a className="video-mark" href={t.videoUrl} target="_blank" rel="noreferrer" title="Open video" onClick={e => e.stopPropagation()}>
            <Icon.video width="15" height="15"/>
          </a>
        )}
        <button className={'foul-toggle ' + (t.foul ? 'on' : '')} onClick={toggleFoul} title="Mark as foul">X</button>
        <button className="menu-btn" onClick={onEditMore} title="More"><Icon.dots width="18" height="18"/></button>
        <button className="menu-btn" onClick={onRemove} title="Delete"><Icon.trash width="16" height="16"/></button>
      </div>
    </div>
  );
}

function ThrowEditor({ t, onChange }) {
  return (
    <div className="detail-grid">
      <div className="field-row">
        <div className="field">
          <label>Distance (m)</label>
          <input
            className="input mono"
            type="text"
            inputMode="decimal"
            placeholder="00.00"
            disabled={t.foul}
            value={t.distance == null ? '' : String(t.distance)}
            onChange={e => {
              const s = e.target.value.replace(',', '.').trim();
              if (s === '') return onChange({ distance: null });
              const v = parseFloat(s);
              if (Number.isFinite(v) || s === '') onChange({ distance: Number.isFinite(v) ? v : null });
            }}
          />
        </div>
        <div className="field" style={{maxWidth:120}}>
          <label>Foul</label>
          <button className={'btn ' + (t.foul ? 'accent' : 'secondary')} onClick={() => onChange({ foul: !t.foul })} style={{height:42}}>
            {t.foul ? 'Marked foul' : 'Mark foul'}
          </button>
        </div>
      </div>
      <div className="field">
        <label>Video link (optional)</label>
        <input className="input" value={t.videoUrl || ''} placeholder="https://… (YouTube, Vimeo, file URL)" onChange={e => onChange({ videoUrl: e.target.value })} />
        <div className="small muted" style={{marginTop:4}}>Paste any URL you can open in a browser.</div>
      </div>
    </div>
  );
}

// ---------- StatsView (combined visualization + season stats) ----------
function StatsView({ data, onOpenComp }) {
  const seasons = useMemo(() => {
    const s = new Set();
    for (const c of data.competitions) { const y = seasonOf(c.date); if (y) s.add(y); }
    return [...s].sort((a,b) => b - a);
  }, [data.competitions]);

  const [season, setSeason] = useState(() => seasons[0] ?? null);
  const [view, setView] = useState('sector'); // 'sector' | 'graph'
  const [selectedComps, setSelectedComps] = useState(() => new Set());
  const [picked, setPicked] = useState(null);

  useEffect(() => {
    if (season != null && !seasons.includes(season) && seasons.length > 0) setSeason(seasons[0]);
    if (season == null && seasons.length > 0) setSeason(seasons[0]);
  }, [seasons]);

  if (data.competitions.length === 0) {
    return (
      <div className="empty">
        <div className="emoji-icon"><Icon.stats/></div>
        <h3>Stats appear once you log throws</h3>
        <p>Add a competition first — your progression, sector map and graph will populate automatically.</p>
      </div>
    );
  }

  // Competitions in this season
  const seasonComps = data.competitions.filter(c => seasonOf(c.date) === season);

  // All throws this season (flattened with competitionId for graph)
  const seasonThrows = useMemo(() => {
    const out = [];
    for (const c of seasonComps) {
      for (const t of c.throws) {
        if (!isAttempted(t)) continue;
        out.push({ ...t, competitionId: c.id });
      }
    }
    return out;
  }, [seasonComps]);

  // Determine majority event in this season — used to set sector angle
  const eventForSector = useMemo(() => {
    const counts = {};
    for (const c of seasonComps) counts[c.event] = (counts[c.event] || 0) + 1;
    let best = 'javelin', bestN = -1;
    for (const [k, v] of Object.entries(counts)) if (v > bestN) { best = k; bestN = v; }
    return best;
  }, [seasonComps]);

  const scaleMax = useMemo(() => {
    const maxD = seasonThrows.filter(t => !t.foul && t.distance != null).reduce((m, t) => Math.max(m, t.distance), 0);
    const evt = EVENTS[eventForSector] || EVENTS.javelin;
    return niceMax(maxD || evt.defaultMax * 0.6, evt.minScale);
  }, [seasonThrows, eventForSector]);

  // Season stats
  const stats = useMemo(() => {
    // PB at start of season = best across all comps BEFORE the first comp of this season
    const seasonStart = seasonComps.map(c => c.date).sort()[0];
    let pbStart = null;
    if (seasonStart) {
      for (const c of data.competitions) {
        if (c.date && c.date < seasonStart) {
          const b = bestThrow(c.throws);
          if (b && (pbStart == null || b.distance > pbStart)) pbStart = b.distance;
        }
      }
    }
    // PB at end of season = best legal throw within season (or all-time up to season end)
    let pbEnd = null;
    let bestObj = null;
    for (const c of seasonComps) {
      const b = bestThrow(c.throws);
      if (b && (pbEnd == null || b.distance > pbEnd)) {
        pbEnd = b.distance;
        bestObj = { distance: b.distance, compName: c.name, date: c.date };
      }
    }
    // If pbEnd < pbStart, take pbStart as pbEnd (PB doesn't go down)
    const finalPbEnd = pbEnd == null ? pbStart : (pbStart != null && pbStart > pbEnd ? pbStart : pbEnd);
    const progression = (finalPbEnd != null && pbStart != null) ? (finalPbEnd - pbStart) : null;
    const attempts = seasonThrows.length;
    const legal = seasonThrows.filter(t => !t.foul).length;
    return {
      pbStart, pbEnd: finalPbEnd, progression,
      best: bestObj,
      attempts, legal,
      comps: seasonComps.length,
    };
  }, [seasonComps, data.competitions, seasonThrows]);

  // Highlighted throws = union of throws from selected comps
  const highlightedThrows = useMemo(() => {
    const s = new Set();
    for (const c of seasonComps) {
      if (selectedComps.has(c.id)) for (const t of c.throws) s.add(t.id);
    }
    return s;
  }, [selectedComps, seasonComps]);

  const toggleComp = (id) => {
    const next = new Set(selectedComps);
    if (next.has(id)) next.delete(id); else next.add(id);
    setSelectedComps(next);
  };

  // When user picks a throw, build a popover near the throw point
  const onPickThrow = (t) => {
    const comp = data.competitions.find(c => c.throws.some(x => x.id === t.id));
    setPicked({ ...t, competitionId: comp?.id, popLeft: 16, popTop: 60 });
  };

  return (
    <Fragment>
      <SeasonSwitcher seasons={seasons} value={season} onChange={(s) => { setSeason(s); setSelectedComps(new Set()); setPicked(null); }} />

      <SeasonHero season={season} stats={stats} />

      <div className="spacer-16"></div>

      <div className="card">
        <div className="flex between items-center" style={{marginBottom:10}}>
          <div className="segmented">
            <button className={view === 'sector' ? 'active' : ''} onClick={() => setView('sector')}>
              <Icon.target/> Sector
            </button>
            <button className={view === 'graph' ? 'active' : ''} onClick={() => setView('graph')}>
              <Icon.chart/> Graph
            </button>
          </div>
          <div className="small muted mono">{view === 'sector' ? `${(EVENTS[eventForSector]||EVENTS.javelin).name} · 0–${scaleMax}m` : `0–${scaleMax}m`}</div>
        </div>

        <div className="vis-frame" onClick={() => setPicked(null)}>
          {view === 'sector'
            ? <SectorView throws={seasonThrows} scaleMax={scaleMax} event={eventForSector} highlightedThrows={highlightedThrows} onPickThrow={onPickThrow} selectedThrowId={picked?.id} />
            : <GraphView throws={seasonThrows} competitions={seasonComps} scaleMax={scaleMax} highlightedThrows={highlightedThrows} onPickThrow={onPickThrow} selectedThrowId={picked?.id} />}
          <ThrowPopover pick={picked} competitions={data.competitions} onClose={() => setPicked(null)} onOpenComp={onOpenComp} />
        </div>
        <div className="legend">
          <span><span className="dot"></span>Throw</span>
          <span><span className="dot accent"></span>Selected comp</span>
          <span><svg className="legend-x" viewBox="0 0 12 12" width="11" height="11" style={{verticalAlign:'-1px', marginRight:5}}><path d="M2 2 L10 10 M10 2 L2 10" stroke="var(--foul)" strokeWidth="1.6" strokeLinecap="round" fill="none"/></svg>Foul</span>
        </div>
      </div>

      <div className="section-label">Highlight competitions</div>
      <div className="chip-list">
        {seasonComps.map(c => (
          <button
            key={c.id}
            className={'chip-pick ' + (selectedComps.has(c.id) ? 'on' : '')}
            onClick={() => toggleComp(c.id)}
          >
            <span className="chip-dot"></span>
            {c.name || 'Untitled'}
            <span className="mono small" style={{opacity:0.6}}>· {formatDate(c.date, { day:'numeric', month:'short' })}</span>
          </button>
        ))}
      </div>
      <div className="spacer-16"></div>
      <div className="small muted" style={{padding:'0 4px 8px'}}>
        Tap a point on the {view} to inspect that throw, or tap a competition above to highlight all of its throws.
      </div>
    </Fragment>
  );
}

function SeasonSwitcher({ seasons, value, onChange }) {
  if (!seasons.length) return null;
  return (
    <div className="season-switch">
      {seasons.map(y => (
        <button key={y} className={value === y ? 'active' : ''} onClick={() => onChange(y)}>{y}</button>
      ))}
    </div>
  );
}

// ---------- SettingsView ----------
function SettingsView({ data, onUpdate }) {
  const toast = useToast();
  const fileRef = useRef(null);
  const { ask, ui: confirmUI } = useConfirm();

  const doExport = () => {
    const payload = { app: 'track-and-throw', version: 1, exportedAt: new Date().toISOString(), data };
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `track-and-throw-${new Date().toISOString().slice(0,10)}.json`;
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
    toast('Exported');
  };

  const doImport = (file) => {
    const r = new FileReader();
    r.onload = async () => {
      try {
        const obj = JSON.parse(r.result);
        const payload = obj.data || obj;
        if (!payload || !Array.isArray(payload.competitions)) throw new Error('Invalid file');
        const ok = await ask({
          title: 'Import data?',
          message: 'This will replace your current data. Make sure to export it first if you want a backup.',
          confirmLabel: 'Replace data',
          danger: true,
        });
        if (!ok) return;
        // Normalize: ensure ids exist
        const competitions = payload.competitions.map(c => ({
          ...c,
          id: c.id || uid('cmp'),
          throws: (c.throws || []).map(t => ({ id: t.id || uid('thr'), distance: t.distance ?? null, foul: !!t.foul, videoUrl: t.videoUrl || '' })),
        }));
        onUpdate({ ...data, ...payload, competitions });
        toast('Imported');
      } catch (e) {
        toast('Could not read file');
      }
    };
    r.readAsText(file);
  };

  const clearAll = async () => {
    const ok = await ask({ title: 'Clear all data?', message: 'All competitions and throws will be permanently removed.', confirmLabel: 'Clear', danger: true });
    if (ok) onUpdate(blankData());
  };

  const loadDemo = async () => {
    if (data.competitions.length > 0) {
      const ok = await ask({ title: 'Replace with demo data?', message: 'Your current data will be lost.', confirmLabel: 'Replace', danger: true });
      if (!ok) return;
    }
    onUpdate({ ...data, competitions: demoSeed().competitions });
  };

  const totalThrows = data.competitions.reduce((n, c) => n + countAttempts(c.throws), 0);
  const totalLegal = data.competitions.reduce((n, c) => n + countLegal(c.throws), 0);

  return (
    <Fragment>
      {confirmUI}
      <div className="section-label">Backup</div>
      <div className="card">
        <p className="small muted" style={{margin:'0 0 12px'}}>
          Your data lives in this browser only. Use Export to save a JSON file and Import on another device to restore it.
        </p>
        <div className="flex gap-8">
          <button className="btn block" onClick={doExport}><Icon.download width="16" height="16"/> Export</button>
          <button className="btn secondary block" onClick={() => fileRef.current?.click()}><Icon.upload width="16" height="16"/> Import</button>
        </div>
        <input ref={fileRef} type="file" accept="application/json,.json" className="hidden" onChange={e => {
          const f = e.target.files?.[0];
          if (f) doImport(f);
          e.target.value = '';
        }}/>
      </div>

      <div className="section-label">Defaults</div>
      <div className="card">
        <div className="field">
          <label>Default event for new competitions</label>
          <select className="select" value={data.settings.defaultEvent} onChange={e => onUpdate({ ...data, settings: { ...data.settings, defaultEvent: e.target.value } })}>
            {Object.entries(EVENTS).map(([k, v]) => <option key={k} value={k}>{v.name}</option>)}
          </select>
        </div>
      </div>

      <div className="section-label">Library</div>
      <div className="card">
        <div className="flex between" style={{marginBottom:6}}>
          <span className="small muted">Competitions</span>
          <span className="mono">{data.competitions.length}</span>
        </div>
        <div className="flex between" style={{marginBottom:6}}>
          <span className="small muted">Throws · legal / attempted</span>
          <span className="mono">{totalLegal} / {totalThrows}</span>
        </div>
        <div className="divider"></div>
        <div className="flex gap-8">
          <button className="btn secondary block" onClick={loadDemo}>Load demo data</button>
          <button className="btn block" style={{background:'#c0392b'}} onClick={clearAll}>Clear all</button>
        </div>
      </div>

      <div className="section-label">About</div>
      <div className="card">
        <div className="small">
          <b>Track &amp; Throw</b>
          <p className="muted" style={{margin:'4px 0 8px'}}>
            A personal throws-event log book. Designed for javelin athletes — works for shot put, discus and hammer too.
          </p>
          <p className="muted" style={{margin:0, fontSize:11.5}}>v1 · static · deployable to GitHub Pages / Cloudflare Pages</p>
        </div>
      </div>
    </Fragment>
  );
}

Object.assign(window, { CompetitionsView, CompetitionDetail, StatsView, SettingsView });
