'use strict';

// Wave 5B — PitcherArsenal component source locks. The card is CONTEXT (the
// arsenal read), never a graded market value. Honesty invariants:
//   • SELF-HIDES (returns null) when Savant has no arsenal — no empty shell.
//   • ALL pitch data (velo / usage / whiff) is mono + tabular (brand rule).
//   • A missing velo/whiff renders "—" (absent), NEVER 0.

const fs = require('fs');
const path = require('path');
const WEB = path.join(__dirname, '..', '..', 'web', 'src');
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');

describe('PitcherArsenal component', () => {
  const src = read('components/vyndr/PitcherArsenal.tsx');

  test('self-hides (returns null) when there are no pitches', () => {
    expect(src).toMatch(/if\s*\(\s*pitches\.length\s*===\s*0\s*\)\s*return null/);
  });

  test('renders the arsenal strip headers (VELO / USE / WHIFF)', () => {
    expect(src).toContain('VELO');
    expect(src).toContain('USE');
    expect(src).toContain('WHIFF');
    expect(src).toContain('PITCH');
  });

  test('pitch data is mono + tabular (brand rule: data is mono)', () => {
    expect(src).toContain('className="mono"');
    expect(src).toContain('tabular-nums');
    // the three data cells map the normalized fields
    expect(src).toContain('fmtVelo(p.velo)');
    expect(src).toContain('fmtPct(p.usagePct)');
    expect(src).toContain('fmtPct(p.whiffPct)');
  });

  test('absent velo/whiff renders an em-dash, never 0 (absent beats zero)', () => {
    // fmtVelo / fmtPct return '—' for null|undefined
    expect(src).toMatch(/function fmtVelo[\s\S]*?===\s*null[\s\S]*?['"]—['"]/);
    expect(src).toMatch(/function fmtPct[\s\S]*?===\s*null[\s\S]*?['"]—['"]/);
    // no `?? 0` / `|| 0` coercion on the numeric fields
    expect(src).not.toMatch(/velo\s*(\?\?|\|\|)\s*0/);
    expect(src).not.toMatch(/whiffPct\s*(\?\?|\|\|)\s*0/);
  });

  test('cites Baseball Savant as the source (honest provenance)', () => {
    expect(src).toMatch(/BASEBALL SAVANT/i);
  });

  test('ranks the strip by the normalized fields from the adapter contract', () => {
    // consumes { found, pitches:[{ type, name, usagePct, velo, whiffPct }] }
    expect(src).toContain('fetched.found');
    expect(src).toContain('fetched.pitches');
  });

  test('is exported from the vyndr barrel', () => {
    const barrel = read('components/vyndr/index.ts');
    expect(barrel).toContain("export { default as PitcherArsenal }");
  });

  test('the arsenal proxy route exists (S25 rule) and forwards to the backend', () => {
    const proxy = read('app/api/stats/pitcher/[name]/arsenal/route.ts');
    expect(proxy).toContain('/api/stats/pitcher/');
    expect(proxy).toContain('/arsenal');
    expect(proxy).toContain('found: false'); // honest fallback, not an error card
  });
});
