// Ball Tagging stage — replaces the old generic post-match report.
// Clones the real CREST scorebook's BALL ANALYSIS and WAGON WHEEL modals
// using the same textures / chip vocabulary. Mandatory fields tagged with
// the "required" pill — the certificate only unlocks once both modals are
// confirmed.

const { useState, useMemo } = React;

// ── Vocab lifted from the real modals ───────────────────────────────────
const LENGTHS = ['Full Toss', 'Yorker', 'Full', 'Good Length', 'Short', 'Bouncer'];
const LINES   = ['Wide (Off)', 'Outside Off', 'Corridor', 'Stumps', 'Leg Drift', 'Wide (Leg)'];
const BALLS_T = ['Outswing', 'Inswing', 'Off-cutter', 'Leg-cutter', 'Slower Ball', 'Other…'];
const FIELDS  = [
  { id: 'brilliance', label: 'Brilliance',     tone: 'green' },
  { id: 'misfield',   label: 'Misfield / Drop', tone: 'red'   },
];
const SHOTS   = ['Straight Drive', 'On Drive', 'Cover Drive', 'Check Drive', 'Lofted Drive', 'Pull', 'Cut', 'Glance', 'Push', 'Other…'];
const DEFS    = ['Defend', 'Push', 'Left Alone']; // shown but disabled when an aggressive SHOT is picked
const FEET    = ['Front Foot', 'Back Foot', 'From Crease'];
const IMPACTS = ['Middled', 'Edged', 'Beaten'];
const INTENTS = ['Ground', 'Lofted'];

// Pre-computed length × line grid for the pitch heat-map. Six columns × six
// length bands. Tone is the colour of the "best" outcome — the user picks
// where the ball pitched.
const PITCH_ZONES = [
  // [length, line, tone]
  ['Yorker',      'Stumps',      'red'    ],
  ['Yorker',      'Corridor',    'orange' ],
  ['Full',        'Stumps',      'orange' ],
  ['Full',        'Corridor',    'green'  ],
  ['Good Length', 'Corridor',    'green'  ],
  ['Good Length', 'Outside Off', 'yellow' ],
  ['Short',       'Outside Off', 'yellow' ],
  ['Short',       'Leg Drift',   'red'    ],
];

// ── BallAnalysisCard — bowler-POV pitch with zone chips ────────────────
function BallAnalysisCard({ data, onChange, onConfirm, bowler, batter, outcome }) {
  const set = (k, v) => onChange({ ...data, [k]: v });
  // Place the ball exactly where the user taps (free position) and derive the
  // LENGTH + LINE names for the chips from that point.
  React.useEffect(() => {
    if (data.pitchX == null) {
      const p = zoneToPos('Full', 'Corridor');
      onChange({ ...data, length: 'Full', line: 'Corridor', pitchX: p.x, pitchY: p.y });
    }
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
  // Click anywhere on the pitch — the ball lands exactly where you tap.
  const pickPitch = (nx, ny) => {
    const { length, line } = pitchPosFromClick(nx, ny);
    onChange({ ...data, length, line, pitchX: nx, pitchY: ny });
  };
  // Picking a LENGTH/LINE chip nudges the ball to that zone's centre.
  const setLen  = (v) => { if (!v) { set('length', null); return; } const p = zoneToPos(v, data.line); onChange({ ...data, length: v, pitchX: p.x, pitchY: p.y }); };
  const setLine = (v) => { if (!v) { set('line', null);  return; } const p = zoneToPos(data.length, v); onChange({ ...data, line: v, pitchX: p.x, pitchY: p.y }); };
  const valid = !!data.length && !!data.line;
  const confirmed = !!data.confirmed;

  return (
    <div className={`bt-card ${confirmed ? 'is-confirmed' : ''}`}>
      <div className="bt-card-head">
        <div className="bt-card-eye">BALL ANALYSIS</div>
        <div className="bt-card-title">
          <b>{bowler || 'N. Cole'}</b> <span className="bt-card-to">to</span> <b>{batter || 'A. Hughes'}</b>
          <span className="bt-card-runs">· {outcome || '1 RUN'}</span>
        </div>
        {confirmed && <span className="bt-card-saved">✓ SAVED</span>}
      </div>

      <div className="bt-card-body">
        {/* Pitch visual — bowler POV */}
        <div className="bt-pitch-wrap">
          <PitchSVG pos={data.pitchX != null ? { x: data.pitchX, y: data.pitchY } : null} onClick={pickPitch}/>
          <div className="bt-pitch-tags">
            <label className="bt-tickbox"><input type="checkbox" defaultChecked/>Grid</label>
            <label className="bt-tickbox"><input type="checkbox" defaultChecked/>Zones</label>
          </div>
          <div className="bt-pitch-hint">
            {valid
              ? <>Pitch: <b>{data.length}</b> · <b>{data.line}</b></>
              : <>Tap the pitch — the ball lands where you click</>
            }
          </div>
        </div>

        {/* Chip groups */}
        <div className="bt-chips-col">
          <ChipGroup
            label="LENGTH" required options={LENGTHS}
            value={data.length} onPick={v => setLen(v)}
            tone="gold"
          />
          <ChipGroup
            label="LINE" required options={LINES}
            value={data.line} onPick={v => setLine(v)}
            tone="blue"
          />
          <ChipGroup
            label="BALL" options={BALLS_T}
            value={data.ballType} onPick={v => set('ballType', v)}
            tone="neutral"
          />
          <div className="bt-field-row">
            <div className="bt-field-lab">FIELD</div>
            <div className="bt-chip-row">
              {FIELDS.map(f => (
                <button
                  key={f.id}
                  className={`bt-chip bt-chip-tone-${f.tone} ${data.field === f.id ? 'is-active' : ''}`}
                  onClick={() => set('field', data.field === f.id ? null : f.id)}
                  type="button"
                >{f.label}</button>
              ))}
            </div>
          </div>
        </div>
      </div>

      <div className="bt-card-foot">
        <span className="bt-foot-status">
          {valid
            ? <>Length: <b>{data.length}</b> · Line: <b>{data.line}</b></>
            : <span className="bt-foot-incomplete">Pick LENGTH and LINE to confirm ↑</span>
          }
        </span>
        <button
          className={`bt-confirm bt-confirm-blue ${!valid ? 'is-disabled' : ''} ${confirmed ? 'is-confirmed' : ''}`}
          onClick={() => valid && onConfirm()}
          disabled={!valid || confirmed}
          type="button"
        >
          {confirmed ? '✓ SAVED' : 'CONFIRM & SAVE'}
        </button>
      </div>
    </div>
  );
}

// ── WagonWheelCard — green field with chip groups ──────────────────────
function WagonWheelCard({ data, onChange, onConfirm, bowler, batter, outcome, kind }) {
  const set = (k, v) => onChange({ ...data, [k]: v });
  // Drag a line from the centre to where the ball travelled. Direction (angle)
  // and length are free; the nearest named sector auto-fills the chips, which
  // the user can still override.
  const onDrag = (p) => {
    const dir = nearestDirection(p.angle);
    const auto = DIRECTION_TO_SHOT[dir] || {};
    onChange({
      ...data,
      wheelPt: { x: p.x, y: p.y },
      wheelDist: p.dist,
      angle: p.angle,
      direction: dir,
      shot:   auto.shot,
      foot:   auto.foot,
      impact: auto.impact,
      intent: auto.intent,
      def:    null,
    });
  };
  // Direction set by dragging the wagon wheel. Required.
  const valid = !!data.shot && !!data.foot && !!data.impact && !!data.intent && !!data.direction;
  const confirmed = !!data.confirmed;

  return (
    <div className={`bt-card ${confirmed ? 'is-confirmed' : ''}`}>
      <div className="bt-card-head">
        <div className="bt-card-eye">WAGON WHEEL</div>
        <div className="bt-card-title">
          <b>{batter || 'A. Hughes'}</b> <span className="bt-card-to">vs</span> <b>{bowler || 'N. Cole'}</b>
          <span className="bt-card-runs">· {outcome || '1 RUN'}</span>
        </div>
        {confirmed && <span className="bt-card-saved">✓ SAVED</span>}
      </div>

      <div className="bt-card-body">
        <div className="bt-wheel-wrap">
          <WagonWheelSVG point={data.wheelPt} onDrag={onDrag} boundary={kind === '4' || kind === '6'}/>
          <div className="bt-wheel-helper">
            {data.wheelPt
              ? <>Shot: <b>{(WHEEL_SECTORS.find(s => s.id === data.direction) || {}).label || data.direction}</b> · drag the line longer / shorter, override chips as needed</>
              : <span className="bt-foot-incomplete">Click the field and drag outward — pull the line to where the ball travelled</span>
            }
            <span className="bt-req">required</span>
          </div>
        </div>

        <div className="bt-chips-col">
          <ChipGroup
            label="SHOT" required options={SHOTS}
            value={data.shot} onPick={v => set('shot', v)}
            tone="gold"
          />
          <div className="bt-field-row">
            <div className="bt-field-lab">
              DEF
              <span className="bt-field-lab-note">{data.shot ? 'N/A — runs scored' : 'Used only on a no-run delivery'}</span>
            </div>
            <div className="bt-chip-row">
              {DEFS.map(d => (
                <button
                  key={d}
                  className={`bt-chip bt-chip-tone-red ${data.def === d ? 'is-active' : ''} ${data.shot ? 'is-disabled' : ''}`}
                  onClick={() => !data.shot && set('def', data.def === d ? null : d)}
                  disabled={!!data.shot}
                  type="button"
                >{d}</button>
              ))}
            </div>
          </div>
          <ChipGroup
            label="FOOT" required options={FEET}
            value={data.foot} onPick={v => set('foot', v)}
            tone="blue"
          />
          <ChipGroup
            label="IMPACT" required options={IMPACTS}
            value={data.impact} onPick={v => set('impact', v)}
            tone="blue"
          />
          <ChipGroup
            label="INTENT" required options={INTENTS}
            value={data.intent} onPick={v => set('intent', v)}
            tone="blue"
          />
        </div>
      </div>

      <div className="bt-card-foot">
        <span className="bt-foot-status">
          {valid
            ? <>{data.shot} · {data.foot} · {data.impact} · {data.intent} · <b>{data.direction}</b></>
            : <span className="bt-foot-incomplete">Tag every required chip + a direction to confirm ↑</span>
          }
        </span>
        <button
          className={`bt-confirm bt-confirm-gold ${!valid ? 'is-disabled' : ''} ${confirmed ? 'is-confirmed' : ''}`}
          onClick={() => valid && onConfirm()}
          disabled={!valid || confirmed}
          type="button"
        >
          {confirmed ? '✓ SAVED' : 'CONFIRM SHOT'}
        </button>
      </div>
    </div>
  );
}

// ── ChipGroup — labeled group of pill buttons w/ optional "required" pill ─
function ChipGroup({ label, options, value, onPick, required, tone = 'neutral' }) {
  return (
    <div className="bt-field-row">
      <div className="bt-field-lab">
        {label} {required && <span className="bt-req">required</span>}
      </div>
      <div className="bt-chip-row">
        {options.map(opt => (
          <button
            key={opt}
            className={`bt-chip bt-chip-tone-${tone} ${value === opt ? 'is-active' : ''}`}
            onClick={() => onPick(value === opt ? null : opt)}
            type="button"
          >{opt}</button>
        ))}
      </div>
    </div>
  );
}

// ── Pitch visual — same assets as the CREST V3 app ────────────────────
// The app's PitchMap / Bowling Analysis panels paint the real turf texture
// (assets/stats/real_pitch_topdown.png) under a perspective pitch and mark the
// ball with the photoreal ball (assets/images/pitch ball.png). This used to be
// flat synthetic art, which read as a different product. Both assets here are
// downscaled copies of the V3 originals (see assets/screens/pitch-texture.jpg).
//
// Coordinates are in the SAME 680×650 space as the art it replaces, so the
// normalised LENGTH_BANDS / LINE_COLUMNS click mapping below is unchanged.
const PITCH_W = 680, PITCH_H = 650;
const PITCH_TOP_Y = 150, PITCH_TOP_L = 247, PITCH_TOP_R = 432;
const PITCH_BOT_Y = 637, PITCH_BOT_L = 41,  PITCH_BOT_R = 638;
// Left/right edge of the pitch quad at a given y.
const pitchEdges = (y) => {
  const t = (y - PITCH_TOP_Y) / (PITCH_BOT_Y - PITCH_TOP_Y);
  return [PITCH_TOP_L + (PITCH_BOT_L - PITCH_TOP_L) * t, PITCH_TOP_R + (PITCH_BOT_R - PITCH_TOP_R) * t];
};
// Screen x for a BASE-normalised x (the un-projected coordinate the line
// columns are measured in), at height y — the inverse of perspK() below.
const pitchProjX = (baseX, y) => PITCH_W * (0.5 + (baseX - 0.5) * perspK(y / PITCH_H));

function PitchSVG({ pos, onClick }) {
  const handleClick = (e) => {
    if (!onClick) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const nx = (e.clientX - rect.left) / rect.width;
    const ny = (e.clientY - rect.top)  / rect.height;
    onClick(Math.max(0, Math.min(1, nx)), Math.max(0, Math.min(1, ny)));
  };
  // Band boundaries sit midway between the label centres, so the zone you can
  // SEE is the zone a click in it reports.
  const bandY = LENGTH_BANDS.map((b) => b.y * PITCH_H);
  const bounds = bandY.slice(0, -1).map((y, i) => (y + bandY[i + 1]) / 2);
  const goodTop = bounds[2], goodBot = bounds[3];
  const [gtL, gtR] = pitchEdges(goodTop);
  const [gbL, gbR] = pitchEdges(goodBot);
  // Column separators — midpoints between the line columns, converging with
  // the pitch toward the batter.
  const colX = LINE_COLUMNS.map((c) => c.x);
  const seps = colX.slice(0, -1).map((x, i) => (x + colX[i + 1]) / 2);

  return (
    <div className="bt-pitch-asset" onClick={handleClick} role={onClick ? 'button' : undefined} aria-label="Click to place the ball — sets length and line">
      <svg className="bt-pitch-svg" viewBox={`0 0 ${PITCH_W} ${PITCH_H}`} role="img" aria-label="Bowler POV pitch with length and line zones">
        <defs>
          <clipPath id="btPitchClip">
            <path d={`M${PITCH_TOP_L},${PITCH_TOP_Y} L${PITCH_TOP_R},${PITCH_TOP_Y} L${PITCH_BOT_R},${PITCH_BOT_Y} L${PITCH_BOT_L},${PITCH_BOT_Y} Z`}/>
          </clipPath>
          {/* Far end sits in shadow — sells the depth the flat art faked. */}
          <linearGradient id="btPitchDepth" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   stopColor="#060810" stopOpacity="0.62"/>
            <stop offset="45%"  stopColor="#060810" stopOpacity="0.24"/>
            <stop offset="100%" stopColor="#060810" stopOpacity="0.05"/>
          </linearGradient>
        </defs>

        {/* Turf — the V3 pitch texture */}
        <g clipPath="url(#btPitchClip)">
          <image href="assets/screens/pitch-texture.jpg" x="0" y="120" width={PITCH_W} height="530" preserveAspectRatio="xMidYMid slice"/>
          <rect x="0" y={PITCH_TOP_Y} width={PITCH_W} height={PITCH_BOT_Y - PITCH_TOP_Y} fill="url(#btPitchDepth)"/>

          {/* Good length — the band every coach talks about */}
          <path d={`M${gtL},${goodTop} L${gtR},${goodTop} L${gbR},${goodBot} L${gbL},${goodBot} Z`} fill="rgba(16,185,129,0.20)"/>

          {/* Length bands */}
          {bounds.map((y, i) => {
            const [l, r] = pitchEdges(y);
            return <line key={`b${i}`} x1={l} y1={y} x2={r} y2={y} stroke="rgba(255,255,255,0.22)" strokeWidth="1.5"/>;
          })}

          {/* Line columns — converge with the pitch */}
          {seps.map((bx, i) => (
            <line
              key={`c${i}`}
              x1={pitchProjX(bx, PITCH_TOP_Y)} y1={PITCH_TOP_Y}
              x2={pitchProjX(bx, PITCH_BOT_Y)} y2={PITCH_BOT_Y}
              stroke="rgba(255,255,255,0.13)" strokeWidth="1.5"
            />
          ))}

          {/* Popping crease at the batting end */}
          <line x1={PITCH_TOP_L + 6} y1={PITCH_TOP_Y + 26} x2={PITCH_TOP_R - 6} y2={PITCH_TOP_Y + 26} stroke="rgba(255,255,255,0.55)" strokeWidth="2"/>
        </g>
        <path
          d={`M${PITCH_TOP_L},${PITCH_TOP_Y} L${PITCH_TOP_R},${PITCH_TOP_Y} L${PITCH_BOT_R},${PITCH_BOT_Y} L${PITCH_BOT_L},${PITCH_BOT_Y} Z`}
          fill="none" stroke="rgba(255,255,255,0.20)" strokeWidth="1.5"
        />

        {/* Stumps at the batting end */}
        <g fill="#E3C08A">
          <rect x="326" y="116" width="4" height="34" rx="1.5"/>
          <rect x="338" y="116" width="4" height="34" rx="1.5"/>
          <rect x="350" y="116" width="4" height="34" rx="1.5"/>
          <rect x="325" y="112" width="14" height="3" rx="1.5"/>
          <rect x="341" y="112" width="14" height="3" rx="1.5"/>
        </g>

        {/* Length labels */}
        {LENGTH_BANDS.map((b) => {
          const y = b.y * PITCH_H;
          const [, r] = pitchEdges(y);
          return (
            <text
              key={b.name}
              x={r - 14} y={y + 5} textAnchor="end"
              fill="#DCE6F2" fontSize="15" fontWeight="700" letterSpacing="1.2"
              stroke="#060810" strokeWidth="3" paintOrder="stroke"
              style={{ fontFamily: 'Inter, sans-serif' }}
            >
              {b.name.toUpperCase()}
            </text>
          );
        })}
        <text x={PITCH_W / 2} y={PITCH_H - 4} textAnchor="middle" fill="#7C8DA3" fontSize="12" letterSpacing="3" style={{ fontFamily: 'Inter, sans-serif' }}>
          BOWLER POV
        </text>
      </svg>
      {pos && (
        <div className="bt-pitch-dot" style={{ left: `${pos.x * 100}%`, top: `${pos.y * 100}%` }} aria-hidden="true">
          <span className="bt-pitch-dot-halo"/>
          <img className="bt-pitch-dot-ball" src="assets/pitch-ball-real.png" alt="" draggable="false"/>
        </div>
      )}
    </div>
  );
}

// ── Wagon wheel — real iPad asset with click-to-pick sectors ─────────
// ── Wagon wheel — real iPad asset with click-to-pick sectors ─────────
// Angles are measured clockwise from NORTH (top of image, 0°). The batter
// sits at the TOP of the pitch facing DOWN (south), so STRAIGHT shots
// travel south (180°). OFF is on the LEFT (west, 270°), LEG on the RIGHT
// (east, 90°). All sector angles below match the labels on the asset.
const WHEEL_SECTORS = [
  { id: 'fine-leg',         angle: 20,  label: 'FINE LEG'     }, // behind on leg → NNE
  { id: 'square-leg',       angle: 60,  label: 'SQUARE LEG'   }, // square on leg → ENE
  { id: 'midwicket',        angle: 100, label: 'MIDWICKET'    }, // forward of square on leg → ESE
  { id: 'long-on',          angle: 140, label: 'LONG ON'      }, // down the ground, leg side → SSE
  { id: 'straight',         angle: 180, label: 'STRAIGHT'     }, // due south → bottom of image
  { id: 'long-off',         angle: 220, label: 'LONG OFF'     }, // down the ground, off side → SSW
  { id: 'cover',            angle: 260, label: 'COVER'        }, // forward of square on off → WSW
  { id: 'point',            angle: 300, label: 'POINT'        }, // square / back-square off → WNW
  { id: 'third-man',        angle: 340, label: 'THIRD MAN'    }, // behind on off → NNW
];

// ── Auto-infer chip values from a wagon-wheel sector pick ──────────────
const DIRECTION_TO_SHOT = {
  'straight':   { shot: 'Straight Drive', foot: 'Front Foot', impact: 'Middled', intent: 'Ground' },
  'long-on':    { shot: 'On Drive',       foot: 'Front Foot', impact: 'Middled', intent: 'Lofted' },
  'long-off':   { shot: 'Lofted Drive',   foot: 'Front Foot', impact: 'Middled', intent: 'Lofted' },
  'cover':      { shot: 'Cover Drive',    foot: 'Front Foot', impact: 'Middled', intent: 'Ground' },
  'point':      { shot: 'Cut',            foot: 'Back Foot',  impact: 'Middled', intent: 'Ground' },
  'third-man':  { shot: 'Cut',            foot: 'Back Foot',  impact: 'Edged',   intent: 'Ground' },
  'fine-leg':   { shot: 'Glance',         foot: 'Back Foot',  impact: 'Middled', intent: 'Ground' },
  'square-leg': { shot: 'Pull',           foot: 'Back Foot',  impact: 'Middled', intent: 'Ground' },
  'midwicket':  { shot: 'Pull',           foot: 'Back Foot',  impact: 'Middled', intent: 'Lofted' },
};

// ── Auto-infer LENGTH + LINE from a pitch click ───────────────────────
// The user clicks anywhere on the pitch art; we map the normalised point back
// to the labelled length bands × line columns from the iPad mock. The ball
// itself is drawn exactly at the click — this only derives the chip values.
const LENGTH_BANDS = [ // top → bottom, normalised y ∈ [0,1] — matched to the art labels
  { y: 0.32, name: 'Full Toss' },
  { y: 0.41, name: 'Yorker' },
  { y: 0.51, name: 'Full' },
  { y: 0.65, name: 'Good Length' },
  { y: 0.81, name: 'Short' },
  { y: 0.90, name: 'Bouncer' },
];
const LINE_COLUMNS = [ // left → right, normalised x ∈ [0,1] (measured at pitch base from the art labels)
  { x: 0.15, name: 'Wide (Off)' },
  { x: 0.28, name: 'Outside Off' },
  { x: 0.41, name: 'Corridor' },
  { x: 0.51, name: 'Stumps' },
  { x: 0.63, name: 'Leg Drift' },
  { x: 0.80, name: 'Wide (Leg)' },
];
// Perspective factor: the pitch is a trapezoid (bowler POV) — full width at the
// base (near camera), converging toward the batsman at top. Fitted to the art:
// K(0.41)=0.67 at the Yorker band, K(0.97)=1.0 at the base.
function perspK(y) {
  const yy = Math.max(0, Math.min(1, y));
  return Math.min(1, 0.43 + 0.59 * yy);
}
function pitchPosFromClick(nx, ny) {
  let length = LENGTH_BANDS[0], best = Infinity;
  for (const b of LENGTH_BANDS) { const d = Math.abs(b.y - ny); if (d < best) { best = d; length = b; } }
  // Un-project x to the base width before matching a line column.
  const ux = 0.5 + (nx - 0.5) / perspK(length.y);
  let line = LINE_COLUMNS[0]; best = Infinity;
  for (const c of LINE_COLUMNS) { const d = Math.abs(c.x - ux); if (d < best) { best = d; line = c; } }
  return { length: length.name, line: line.name };
}
// Inverse: zone names → a normalised point on the pitch (for chip-driven moves).
function zoneToPos(length, line) {
  const b = LENGTH_BANDS.find(o => o.name === length);
  const c = LINE_COLUMNS.find(o => o.name === line);
  const y = b ? b.y : 0.53;
  const baseX = c ? c.x : 0.45;
  const x = 0.5 + (baseX - 0.5) * perspK(y);
  return { x, y };
}
// Nearest named direction for a continuous drag angle (clockwise from north).
function nearestDirection(angle) {
  let best = WHEEL_SECTORS[0], bd = Infinity;
  for (const s of WHEEL_SECTORS) {
    const d = Math.abs(((s.angle - angle + 540) % 360) - 180);
    if (d < bd) { bd = d; best = s; }
  }
  return best.id;
}
function WagonWheelSVG({ point, onDrag, boundary }) {
  // Field is the V3 app's own grass asset (assets/stats/field_wagon_wheel.jpg —
  // the same image the Flutter InteractiveWagonWheel and the site's
  // /crest-vision renderer paint), 700×700, circle centred (350,348) r=343,
  // measured from the file. The STRIKER (batting end) sits ~10% of the radius
  // above centre — the same CREASE_FRAC the app uses — and every shot line
  // radiates from THERE, not the geometric field centre. STRAIGHT is down the
  // ground (south).
  const ref = React.useRef(null);
  const ox = 350, oy = 314;             // striker / batting end — line origin
  const fcx = 350, fcy = 348, fR = 336; // field circle, used only to clamp the drag
  // Extend a ray from the striker (in direction dx,dy) all the way to the
  // boundary rope — used for 4s and 6s so the line always reaches the edge.
  const toBoundary = (dx, dy) => {
    const len = Math.hypot(dx, dy) || 1;
    const ux = dx / len, uy = dy / len;
    const ocx = ox - fcx, ocy = oy - fcy;
    const b = 2 * (ux * ocx + uy * ocy);
    const cc = ocx * ocx + ocy * ocy - fR * fR;
    const disc = Math.sqrt(Math.max(0, b * b - 4 * cc));
    const t = (-b + disc) / 2;
    return { x: ox + ux * t, y: oy + uy * t };
  };
  const calc = (clientX, clientY) => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    let x = (clientX - rect.left) / rect.width  * 700;
    let y = (clientY - rect.top)  / rect.height * 700;
    // Keep the landing point inside the field circle.
    const cdx = x - fcx, cdy = y - fcy, cd = Math.hypot(cdx, cdy);
    if (cd > fR) { x = fcx + cdx * fR / cd; y = fcy + cdy * fR / cd; }
    let dx = x - ox, dy = y - oy;
    const angle = (Math.atan2(dy, dx) * 180 / Math.PI + 90 + 360) % 360;
    // Boundary: the drag only picks the ANGLE — the line runs to the rope.
    if (boundary) { const bpt = toBoundary(dx, dy); x = bpt.x; y = bpt.y; }
    const dist = Math.hypot(x - ox, y - oy);
    onDrag({ x, y, dist: dist / (fR * 2), angle });
  };
  const onDown = (e) => {
    e.preventDefault();
    calc(e.clientX, e.clientY);
    const move = (ev) => calc(ev.clientX, ev.clientY);
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  };
  return (
    <div className="bt-wheel-asset" ref={ref}>
      <svg viewBox="0 0 700 700" className="bt-wheel-overlay" onPointerDown={onDown} aria-label="Click the field and drag outward to set shot direction and distance">
        <defs>
          <clipPath id="btWheelClip"><circle cx={fcx} cy={fcy} r="343"/></clipPath>
        </defs>
        {/* Grass — the V3 field asset, clipped to the rope (the file has white
            corners that would glare against the dark card). */}
        <image href="assets/screens/field-wagon-wheel.jpg" x="0" y="0" width="700" height="700" clipPath="url(#btWheelClip)" preserveAspectRatio="xMidYMid slice"/>
        <circle cx={fcx} cy={fcy} r="343" fill="none" stroke="rgba(255,255,255,0.35)" strokeWidth="2"/>
        {/* Full-area drag surface */}
        <rect x="0" y="0" width="700" height="700" fill="rgba(0,0,0,0.001)" style={{ cursor: 'crosshair' }}/>
        {/* Sector names — the grass carries the lines, we carry the labels */}
        <g style={{ pointerEvents: 'none' }}>
          {WHEEL_SECTORS.map((s) => {
            const a = s.angle * Math.PI / 180;
            // 0.76 keeps the widest labels (SQUARE LEG / MIDWICKET) inside the rope.
            const r = 343 * 0.76;
            return (
              <text
                key={s.id}
                x={fcx + r * Math.sin(a)} y={fcy - r * Math.cos(a) + 4}
                textAnchor="middle"
                fill="#F2F6FB" fontSize="16" fontWeight="700" letterSpacing="1.1"
                stroke="rgba(6,8,16,0.85)" strokeWidth="3.5" paintOrder="stroke"
                style={{ fontFamily: 'Inter, sans-serif' }}
              >
                {s.label}
              </text>
            );
          })}
        </g>
        {/* The shot line starts at the striker (batting end) and follows the pointer */}
        {point && (
          <g style={{ pointerEvents: 'none' }}>
            <line x1={ox} y1={oy} x2={point.x} y2={point.y} stroke="#FCD34D" strokeWidth="5" strokeLinecap="round"/>
            <circle cx={point.x} cy={point.y} r="11" fill="#FCD34D" stroke="#060810" strokeWidth="2.5"/>
          </g>
        )}
        <circle cx={ox} cy={oy} r="7" fill="#FCD34D" stroke="#060810" strokeWidth="2.5" style={{ pointerEvents: 'none' }}/>
      </svg>
    </div>
  );
}

// ── Top-level stage ────────────────────────────────────────────────────
function BallTaggingStage({ finalState, matchDetails, onFinish, onBack, onSkip }) {
  const [ba, setBA] = useState({});       // Ball Analysis form data
  const [ww, setWW] = useState({});       // Wagon Wheel form data
  const [tab, setTab] = useState('ba');   // 'ba' | 'ww'

  const baConfirmed = !!ba.confirmed;
  const wwConfirmed = !!ww.confirmed;
  const allConfirmed = baConfirmed && wwConfirmed;

  return (
    <div className="bt-stage">
      <div className="bt-banner">
        <div className="bt-banner-left">
          <div className="bt-banner-eye">★ INNINGS COMPLETE · TAG THE STANDOUT BALL</div>
          <div className="bt-banner-title">Two intel modals · one delivery</div>
          <div className="bt-banner-sub">
            Real scorers tag standout deliveries with <b>Ball Analysis</b> and <b>Wagon Wheel</b> — the
            data that powers Pitch Maps, Beehive, Hawk-Eye and Strokeplay panels in the live app.
          </div>
        </div>
        <div className="bt-banner-right">
          <div className="bt-banner-progress">
            <div className="bt-banner-prog-row">
              <span className={baConfirmed ? 'is-done' : ''}>
                {baConfirmed ? '✓' : '○'} BALL ANALYSIS
              </span>
              <span className={wwConfirmed ? 'is-done' : ''}>
                {wwConfirmed ? '✓' : '○'} WAGON WHEEL
              </span>
            </div>
            <div className="bt-banner-prog-bar">
              <div style={{width: `${((+baConfirmed + +wwConfirmed) / 2) * 100}%`}}/>
            </div>
          </div>
          {allConfirmed ? (
            <button data-cert="pm-generate" className="bt-banner-cta gold" onClick={onFinish}>
              ★ GENERATE CERTIFICATE
              <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
            </button>
          ) : (
            <div className="bt-banner-cta locked">
              <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg>
              <span>CONFIRM BOTH TO UNLOCK</span>
            </div>
          )}
        </div>
      </div>

      <div className="bt-tabs">
        <button className={`bt-tab ${tab === 'ba' ? 'is-active' : ''} ${baConfirmed ? 'is-done' : ''}`} onClick={() => setTab('ba')}>
          <span className="bt-tab-num">01</span>
          <span className="bt-tab-name">BALL ANALYSIS</span>
          {baConfirmed && <span className="bt-tab-tick">✓</span>}
        </button>
        <button className={`bt-tab ${tab === 'ww' ? 'is-active' : ''} ${wwConfirmed ? 'is-done' : ''}`} onClick={() => setTab('ww')}>
          <span className="bt-tab-num">02</span>
          <span className="bt-tab-name">WAGON WHEEL</span>
          {wwConfirmed && <span className="bt-tab-tick">✓</span>}
        </button>
      </div>

      <div className="bt-body">
        {tab === 'ba' && (
          <BallAnalysisCard
            data={ba}
            onChange={setBA}
            onConfirm={() => {
              setBA(d => ({ ...d, confirmed: true }));
              if (!wwConfirmed) setTimeout(() => setTab('ww'), 400);
            }}
          />
        )}
        {tab === 'ww' && (
          <WagonWheelCard
            data={ww}
            onChange={setWW}
            onConfirm={() => setWW(d => ({ ...d, confirmed: true }))}
          />
        )}
      </div>

      <div className="bt-foot">
        {onBack && (
          <button className="bt-foot-back" onClick={onBack}>
            <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
            Back to scoring
          </button>
        )}
        <button className="bt-foot-skip" onClick={onSkip}>Skip tagging →</button>
      </div>
    </div>
  );
}

window.BallTaggingStage = BallTaggingStage;
window.BallAnalysisCard = BallAnalysisCard;
window.WagonWheelCard   = WagonWheelCard;
