/**
 * Tests for the headless BPM-detector helper (dj/bpmDetector.ts).
 *
 * Context: DJ BPM analysis used realtime-bpm-analyzer (browser Web Audio),
 * which is dead in the headless Node server — verified that 0% of the
 * library had been analyzed since the Electron→headless migration, and
 * OfflineAudioContext/AudioWorklet are undefined in Node. It now shells out
 * to bpm_detector_simple.py (ffmpeg + stdlib). These cover the pure pieces:
 * python-path resolution and stdout parsing (the execFile call itself is an
 * integration concern, validated manually against a real track).
 */ import { describe, it, expect, afterEach } from "@jest/globals";
import { resolvePython3Path, parseBpmDetectorOutput, buildDetectorPath, BPM_DETECTOR_SCRIPT } from "../dj/bpmDetector.js";
describe("resolvePython3Path", ()=>{
    const savedEnv = process.env.SOUND_PYTHON3;
    afterEach(()=>{
        if (savedEnv === undefined) delete process.env.SOUND_PYTHON3;
        else process.env.SOUND_PYTHON3 = savedEnv;
    });
    it("honors SOUND_PYTHON3 when set", ()=>{
        process.env.SOUND_PYTHON3 = "/custom/python3";
        expect(resolvePython3Path()).toBe("/custom/python3");
    });
    it("falls back to a platform default when SOUND_PYTHON3 is unset", ()=>{
        delete process.env.SOUND_PYTHON3;
        const p = resolvePython3Path();
        if (process.platform === "darwin") {
            // Homebrew python3 (Apple Silicon /opt/homebrew or Intel /usr/local) if
            // present, else bare "python3" on PATH.
            expect([
                "/opt/homebrew/bin/python3",
                "/usr/local/bin/python3",
                "python3"
            ]).toContain(p);
        } else if (process.platform === "win32") {
            // Probes python3 → python (Store vs python.org); best-effort "python".
            expect([
                "python3",
                "python"
            ]).toContain(p);
        } else {
            expect(p).toBe("python3"); // linux / Docker — python3 on PATH
        }
    });
});
describe("buildDetectorPath", ()=>{
    it("joins with `;` and prepends bin\\win32 (no homebrew) on Windows", ()=>{
        const result = buildDetectorPath("win32", "C:\\SOUND-daemon", "C:\\Windows\\System32");
        expect(result).toBe("C:\\SOUND-daemon\\bin\\win32;C:\\Windows\\System32");
        // Regression guard: the bundled bin dir must be its own intact entry, not
        // fused with the next one (the `:`-join bug that broke ffmpeg on Windows).
        expect(result.split(";")[0]).toBe("C:\\SOUND-daemon\\bin\\win32");
        // Windows never gets the POSIX homebrew/system dirs.
        expect(result).not.toContain("/opt/homebrew/bin");
    });
    it("prepends bin/darwin + Homebrew/system dirs joined with `:` on macOS", ()=>{
        const result = buildDetectorPath("darwin", "/repo", "/usr/bin:/bin");
        expect(result).toBe("/repo/bin/darwin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/usr/bin:/bin");
        expect(result.split(":")[0]).toBe("/repo/bin/darwin");
    });
    it("prepends bin/linux + system dirs on Linux (Docker/alpine)", ()=>{
        const result = buildDetectorPath("linux", "/app", "/usr/bin");
        expect(result).toBe("/app/bin/linux:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/usr/bin");
    });
    it("omits a trailing empty segment when the inherited PATH is empty", ()=>{
        expect(buildDetectorPath("win32", "C:\\app", "")).toBe("C:\\app\\bin\\win32");
        expect(buildDetectorPath("linux", "/app", "")).toBe("/app/bin/linux:/opt/homebrew/bin:/usr/local/bin:/usr/bin");
    });
});
describe("BPM_DETECTOR_SCRIPT path", ()=>{
    it("points at scripts/bpm_detector_simple.py (resolves under ../scripts)", ()=>{
        expect(BPM_DETECTOR_SCRIPT).toMatch(/scripts[/\\]bpm_detector_simple\.py$/);
    });
});
describe("parseBpmDetectorOutput", ()=>{
    it("parses a valid detector result and rounds bpm", ()=>{
        const r = parseBpmDetectorOutput('{"bpm": 171.4, "confidence": 0.3, "method": "onset_detection"}');
        expect(r).toEqual({
            bpm: 171,
            confidence: 0.3
        });
    });
    it("rounds bpm to the nearest integer", ()=>{
        expect(parseBpmDetectorOutput('{"bpm": 127.6, "confidence": 0.9}').bpm).toBe(128);
    });
    describe("returns {0,0} for unusable output (never throws)", ()=>{
        const bad = [
            [
                "empty string",
                ""
            ],
            [
                "whitespace",
                "   \n "
            ],
            [
                "malformed json",
                "{not json"
            ],
            [
                "bpm zero",
                '{"bpm": 0, "confidence": 0.5}'
            ],
            [
                "bpm negative",
                '{"bpm": -120, "confidence": 0.5}'
            ],
            [
                "bpm NaN",
                '{"bpm": "fast", "confidence": 0.5}'
            ],
            [
                "bpm missing",
                '{"confidence": 0.5}'
            ],
            [
                "null bpm",
                '{"bpm": null}'
            ],
            [
                "empty object",
                "{}"
            ]
        ];
        for (const [label, input] of bad){
            it(label, ()=>{
                expect(parseBpmDetectorOutput(input)).toEqual({
                    bpm: 0,
                    confidence: 0
                });
            });
        }
    });
    it("clamps a non-finite/negative confidence to 0 while keeping a valid bpm", ()=>{
        expect(parseBpmDetectorOutput('{"bpm": 120, "confidence": -1}')).toEqual({
            bpm: 120,
            confidence: 0
        });
        expect(parseBpmDetectorOutput('{"bpm": 120}')).toEqual({
            bpm: 120,
            confidence: 0
        });
    });
    it("tolerates surrounding whitespace/newlines from the script", ()=>{
        expect(parseBpmDetectorOutput('\n  {"bpm": 90, "confidence": 0.7}  \n')).toEqual({
            bpm: 90,
            confidence: 0.7
        });
    });
});

//# sourceMappingURL=bpmDetector.test.js.map