// ScorecardImportSection.jsx — snap KCBS point sheets / comment cards from a
// phone, POST the images to /api/extract-scorecard, preview the returned JSON,
// optionally merge into an event.

const MEATS = ['chicken', 'ribs', 'pork', 'brisket'];

async function shrinkImage(file, maxSide = 1568, quality = 0.85) {
  const bmp = await createImageBitmap(file);
  const scale = Math.min(1, maxSide / Math.max(bmp.width, bmp.height));
  const canvas = document.createElement('canvas');
  canvas.width = Math.round(bmp.width * scale);
  canvas.height = Math.round(bmp.height * scale);
  canvas.getContext('2d').drawImage(bmp, 0, 0, canvas.width, canvas.height);
  const blob = await new Promise(res => canvas.toBlob(res, 'image/jpeg', quality));
  const dataUrl = await new Promise((res, rej) => {
    const r = new FileReader();
    r.onload = () => res(r.result);
    r.onerror = () => rej(new Error('FileReader failed'));
    r.readAsDataURL(blob);
  });
  const b64 = String(dataUrl).split(',')[1] || '';
  return { media_type: 'image/jpeg', data: b64, thumb: dataUrl, name: file.name };
}

const ScorecardImportSection = ({ onBanner }) => {
  const eventsStore = useEventsStore();
  const [files, setFiles] = useState([]); // [{ media_type, data, thumb, name }]
  const [note, setNote] = useState('');
  const [eventId, setEventId] = useState('');
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState(null); // { cards, warnings }
  const [usage, setUsage] = useState(null);
  const [error, setError] = useState('');

  const eligible = eventsStore.events.filter(e => !e.done || (e.done && !e.place));

  const onPick = async (e) => {
    const picked = Array.from(e.target.files || []);
    e.target.value = ''; // allow re-picking same file
    if (!picked.length) return;
    setBusy(true); setError('');
    try {
      const next = [];
      for (const f of picked) {
        if (!/^image\//.test(f.type)) continue;
        // eslint-disable-next-line no-await-in-loop
        next.push(await shrinkImage(f));
      }
      setFiles(prev => [...prev, ...next].slice(0, 12));
    } catch (err) {
      setError(String(err.message || err));
    } finally {
      setBusy(false);
    }
  };

  const removeFile = (i) => setFiles(files.filter((_, idx) => idx !== i));

  const extract = async () => {
    if (!files.length) return;
    setBusy(true); setError(''); setResult(null); setUsage(null);
    try {
      const r = await fetch('/api/extract-scorecard', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          images: files.map(f => ({ media_type: f.media_type, data: f.data })),
          note,
        }),
      });
      const body = await r.json().catch(() => null);
      if (!r.ok || !body || !body.ok) {
        const msg = (body && (body.error?.message || body.error)) || `HTTP ${r.status}`;
        throw new Error(typeof msg === 'string' ? msg : JSON.stringify(msg));
      }
      setResult(body.result || { cards: [], warnings: [] });
      setUsage(body.usage || null);
      onBanner({ kind: 'ok', msg: `Extracted ${(body.result?.cards || []).length} card(s).` });
    } catch (err) {
      setError(String(err.message || err));
      onBanner({ kind: 'err', msg: String(err.message || err) });
    } finally {
      setBusy(false);
    }
  };

  const downloadJson = () => {
    if (!result) return;
    const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `scorecards-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
    document.body.appendChild(a); a.click(); a.remove();
    URL.revokeObjectURL(url);
  };

  // Collapse point_sheet cards into the { results } shape EventsStore.update expects.
  const applyToEvent = async () => {
    const ev = eventsStore.events.find(e => e.id === eventId);
    if (!ev) { onBanner({ kind: 'err', msg: 'Pick an event first.' }); return; }
    const cards = (result && result.cards) || [];
    const overall = (result && result.overall) || {};
    const nextResults = { ...(ev.results || {}) };
    let applied = 0;
    for (const c of cards) {
      if (c.card_type !== 'point_sheet') continue;
      if (!MEATS.includes(c.category)) continue;
      const place = c.place ? String(c.place) : '';
      const score = c.weighted_total || c.raw_total;
      if (!place && !score) continue;
      nextResults[c.category] = {
        place: place || (nextResults[c.category] && nextResults[c.category].place) || '',
        score: score != null ? String(score) : (nextResults[c.category] && nextResults[c.category].score) || '',
      };
      applied++;
    }
    const next = { ...ev, done: true, status: 'DONE', results: nextResults };
    let overallApplied = 0;
    if (overall.place)       { next.place = String(overall.place).replace(/\D+$/, '') || String(overall.place); overallApplied++; }
    if (overall.score != null) { next.score = String(overall.score); overallApplied++; }
    if (overall.total_teams)   { next.teams = String(overall.total_teams); overallApplied++; }
    if (!applied && !overallApplied) {
      onBanner({ kind: 'err', msg: 'No point_sheet cards or overall data to apply.' });
      return;
    }
    setBusy(true);
    try {
      await window.EventsStore.update(next);
      onBanner({ kind: 'ok', msg: `Applied ${applied} meat result(s)${overallApplied ? ' + overall' : ''} to "${ev.name}".` });
    } catch (err) {
      onBanner({ kind: 'err', msg: String(err.message || err) });
    } finally {
      setBusy(false);
    }
  };

  const canExtract = files.length > 0 && !busy;
  const hasApplyablePerMeat = result && (result.cards || []).some(c => c.card_type === 'point_sheet' && MEATS.includes(c.category));
  const hasOverall = result && result.overall && (result.overall.place || result.overall.score || result.overall.total_teams);
  const canApply = (hasApplyablePerMeat || hasOverall) && eventId;

  return (
    <>
      <PageHero
        eyebrow="Admin · Scorecards"
        title={<>Snap <span style={{ color: 'var(--wbbq-flame)' }}>Point Sheets.</span></>}
        subtitle="Phone photo in. Structured JSON out."
        mascot="robot-monkey.svg"
      />

      <div style={{ marginTop: 28, display: 'grid', gap: 16 }}>
        <div>
          <AdminLabel>Upload cards</AdminLabel>
          <input
            type="file"
            accept="image/*"
            multiple
            onChange={onPick}
            style={{ ...editorInputStyle(), padding: '10px 12px', fontFamily: 'var(--font-mono)', fontSize: 12 }}
          />
          <div style={{ fontSize: 11, color: 'var(--fg3)', marginTop: 6 }}>Up to 12 images per batch. They're resized to 1568px on-device before upload.</div>
        </div>

        {files.length > 0 && (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>
            {files.map((f, i) => (
              <div key={i} style={{ position: 'relative', width: 96, height: 128, border: '2px solid var(--border)', background: 'var(--wbbq-black)' }}>
                <img src={f.thumb} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                <button
                  onClick={() => removeFile(i)}
                  style={{
                    position: 'absolute', top: 2, right: 2,
                    background: 'var(--wbbq-blood)', color: 'var(--wbbq-bone)', border: 0,
                    width: 22, height: 22, cursor: 'pointer', fontSize: 12, lineHeight: '22px',
                  }}
                >✕</button>
              </div>
            ))}
          </div>
        )}

        <div>
          <AdminLabel>Contest hint (optional)</AdminLabel>
          <input
            type="text"
            value={note}
            onChange={(e) => setNote(e.target.value)}
            placeholder="e.g. Kansas Speedway · 2026-04-12"
            style={editorInputStyle()}
          />
        </div>

        <div>
          <AdminLabel>Apply to event (optional)</AdminLabel>
          <select value={eventId} onChange={(e) => setEventId(e.target.value)} style={editorInputStyle()}>
            <option value="">— select event —</option>
            {eligible.map(e => (
              <option key={e.id} value={e.id}>{e.date} {e.year} · {e.name}</option>
            ))}
          </select>
        </div>

        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
          <button onClick={extract} disabled={!canExtract} className="btn btn-primary" style={{ fontSize: 13, opacity: canExtract ? 1 : 0.4 }}>
            {busy && !result ? 'Extracting…' : 'Extract →'}
          </button>
          <button onClick={() => { setFiles([]); setResult(null); setError(''); setUsage(null); }} className="btn btn-ghost" style={{ fontSize: 13 }}>Clear</button>
        </div>

        {error && (
          <div style={{ padding: 12, background: 'var(--wbbq-black)', border: '2px solid var(--wbbq-blood)', color: 'var(--wbbq-blood)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>
            ⚠ {error}
          </div>
        )}

        {result && (
          <div style={{ display: 'grid', gap: 14 }}>
            <div style={{ padding: 14, background: 'var(--wbbq-ink)', border: '2px solid var(--wbbq-slime)' }}>
              <div className="eyebrow" style={{ color: 'var(--wbbq-slime)' }}>◉ {(result.cards || []).length} card(s) extracted</div>
              {usage && (
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg3)', marginTop: 6 }}>
                  tokens: in {usage.input_tokens ?? '—'} · out {usage.output_tokens ?? '—'}
                  {usage.cache_read_input_tokens != null ? ` · cache-read ${usage.cache_read_input_tokens}` : ''}
                </div>
              )}
              {(result.warnings || []).map((w, i) => (
                <div key={i} style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--wbbq-flame)', marginTop: 6 }}>⚠ {w}</div>
              ))}
            </div>

            {result.overall && (result.overall.place || result.overall.score || result.overall.total_teams) && (
              <div style={{ padding: 14, background: 'var(--wbbq-black)', border: '2px solid var(--wbbq-flame)' }}>
                <div className="eyebrow" style={{ color: 'var(--wbbq-flame)' }}>◉ Overall</div>
                <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, color: 'var(--fg1)', marginTop: 6 }}>
                  place {result.overall.place || '—'} · score {result.overall.score ?? '—'} · teams {result.overall.total_teams ?? '—'}
                  {result.overall.contest_name ? ` · ${result.overall.contest_name}` : ''}
                </div>
              </div>
            )}

            <div style={{ display: 'grid', gap: 10 }}>
              {(result.cards || []).map((c, i) => <CardSummary key={i} card={c} />)}
            </div>

            <details>
              <summary style={{ cursor: 'pointer', fontFamily: 'var(--font-display)', fontSize: 12, letterSpacing: '0.1em', color: 'var(--fg2)' }}>RAW JSON</summary>
              <pre style={{
                marginTop: 10, padding: 14, background: 'var(--wbbq-black)', border: '2px solid var(--border)',
                fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg1)',
                overflow: 'auto', maxHeight: 420,
              }}>{JSON.stringify(result, null, 2)}</pre>
            </details>

            <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
              <button onClick={downloadJson} className="btn btn-ghost" style={{ fontSize: 13 }}>Download JSON</button>
              <button onClick={applyToEvent} disabled={!canApply || busy} className="btn btn-primary" style={{ fontSize: 13, opacity: (canApply && !busy) ? 1 : 0.4 }}>
                {busy ? 'Applying…' : 'Apply to event →'}
              </button>
            </div>
          </div>
        )}
      </div>
    </>
  );
};
window.ScorecardImportSection = ScorecardImportSection;

const CardSummary = ({ card }) => {
  const tone =
    card.confidence === 'high' ? 'var(--wbbq-slime)' :
    card.confidence === 'low' ? 'var(--wbbq-flame)' : 'var(--wbbq-blue)';
  return (
    <div style={{ padding: 14, background: 'var(--wbbq-ink)', border: '2px solid var(--border)' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 10, flexWrap: 'wrap' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 14, color: 'var(--wbbq-bone)', letterSpacing: '0.08em' }}>
          {String(card.card_type || 'unknown').toUpperCase()} · {String(card.category || 'unknown').toUpperCase()}
          {card.ancillary_name ? ` · ${card.ancillary_name}` : ''}
        </div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: tone }}>
          {card.confidence ? `conf: ${card.confidence}` : ''}
        </div>
      </div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg2)', marginTop: 6 }}>
        {card.team_number ? `team #${card.team_number}` : ''}
        {card.team_name ? ` · ${card.team_name}` : ''}
        {card.turn_in_time ? ` · turn-in ${card.turn_in_time}` : ''}
        {card.table_number ? ` · table ${card.table_number}` : ''}
      </div>
      {(card.weighted_total || card.raw_total || card.place) && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg1)', marginTop: 4 }}>
          {card.place ? `place ${card.place}` : ''}
          {card.weighted_total ? ` · weighted ${card.weighted_total}` : ''}
          {card.raw_total ? ` · raw ${card.raw_total}` : ''}
        </div>
      )}
      {Array.isArray(card.judges) && card.judges.length > 0 && (
        <div style={{ marginTop: 10, display: 'grid', gap: 4 }}>
          {card.judges.map((j, i) => (
            <div key={i} style={{ fontFamily: 'var(--font-mono)', fontSize: 12, color: j.dq ? 'var(--wbbq-blood)' : 'var(--fg1)' }}>
              #{j.judge_number ?? (i + 1)}
              {j.judge_id ? ` (${j.judge_id})` : ''}
              {j.dq
                ? ` · DQ${j.dq_reason ? ` — ${j.dq_reason}` : ''}`
                : ` · app ${j.appearance ?? '—'} · taste ${j.taste ?? '—'} · tender ${j.tenderness ?? '—'}`
              }
              {j.comments && (j.comments.appearance || j.comments.taste || j.comments.tenderness || j.comments.overall) ? (
                <div style={{ color: 'var(--fg3)', marginLeft: 14, whiteSpace: 'pre-wrap' }}>
                  {j.comments.appearance ? `A: ${j.comments.appearance}\n` : ''}
                  {j.comments.taste ? `T: ${j.comments.taste}\n` : ''}
                  {j.comments.tenderness ? `N: ${j.comments.tenderness}\n` : ''}
                  {j.comments.overall ? `O: ${j.comments.overall}` : ''}
                </div>
              ) : null}
            </div>
          ))}
        </div>
      )}
      {card.notes && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg3)', marginTop: 8 }}>note: {card.notes}</div>
      )}
    </div>
  );
};
window.ScorecardCardSummary = CardSummary;
