// Verify static/highway.js handles the `handshapes` WebSocket message:
// accumulates incoming chunks, time-sorts on `ready`, and exposes the
// result on the renderer bundle. The 3D arpeggio frame / chord rails
// pipeline consumes that bundle; if accumulation or sort drifts, those
// visuals silently render wrong (out-of-order frames, missing hints).
//
// Same source-level guard strategy as the other tests/js/ files —
// extract the relevant case body and assert the wiring.

const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');

const HIGHWAY_JS = path.join(__dirname, '..', '..', 'static', 'highway.js');

function getCaseBlock(src, label) {
    const start = src.indexOf(`case '${label}'`);
    assert.ok(start !== -1, `case '${label}' not found in highway.js`);
    const tail = src.slice(start);
    const nextCase = tail.search(/\n\s*case\s+['"]/);
    const nextDefault = tail.search(/\n\s*default\s*:/);
    let end = tail.length;
    if (nextCase > 0) end = Math.min(end, nextCase);
    if (nextDefault > 0) end = Math.min(end, nextDefault);
    return tail.slice(0, end);
}

test('handshapes WS case accumulates incoming chunks into handShapes', () => {
    // Server streams handshapes in chunks; the case must concat rather
    // than replace so multi-chunk sources don't silently truncate.
    const src = fs.readFileSync(HIGHWAY_JS, 'utf8');
    const block = getCaseBlock(src, 'handshapes');
    assert.match(
        block,
        /handShapes\s*=\s*handShapes\.concat\(\s*msg\.data\s*\)/,
        'handshapes case must concat msg.data into the handShapes accumulator',
    );
});

test('ready case time-sorts handShapes before rendering', () => {
    // Out-of-order chunks would otherwise leave handShapes interleaved,
    // breaking the binary-search lookups the 3D renderer does when
    // probing arpeggio coverage for a chord at time t.
    const src = fs.readFileSync(HIGHWAY_JS, 'utf8');
    const block = getCaseBlock(src, 'ready');
    assert.match(
        block,
        /handShapes\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\.start_time\s*-\s*b\.start_time\s*\)/,
        'ready case must sort handShapes by start_time so the renderer can rely on ordering',
    );
});

test('bundle exposes handShapes to renderers with flat-list fallback', () => {
    // Renderers (highway_3d in particular) read `bundle.handShapes`; if
    // the bundle key gets renamed or dropped, the 3D arp-frame pipeline
    // goes dark without a runtime error. The current shape is a ternary
    // that picks `_filteredHandShapes` when phrase data carries any and
    // falls back to the flat `handShapes` list otherwise (DLC pattern).
    // Pin both sides of the ternary so accidentally dropping the
    // fallback branch fails the test.
    const src = fs.readFileSync(HIGHWAY_JS, 'utf8');
    assert.match(
        src,
        /\bhandShapes:\s*\([^)]*_filteredHandShapes[^)]*\)\s*\?\s*_filteredHandShapes\s*:\s*handShapes\b/,
        'bundle must expose handShapes with the _filteredHandShapes-vs-handShapes ternary fallback',
    );
});
