/**
 * linux-ready.test.ts — Tests for Linux voice readiness logic (#2403).
 *
 * Covers:
 *   - diagnoseSounddeviceError branch ordering (ModuleNotFoundError must NOT
 *     match the portaudio branch, even though it contains "sounddevice")
 *   - ensureVoiceVenv auto-creation
 *   - linuxPython venv detection
 */

import assert from "node:assert/strict";
import { describe, it } from "vitest";
import { diagnoseSounddeviceError, ensureVoiceVenv } from "../linux-ready.ts";

describe("diagnoseSounddeviceError", () => {
	it("ModuleNotFoundError must return missing-module", () => {
		const stderr =
			"Traceback (most recent call last):\n  File \"<string>\", line 1, in <module>\nModuleNotFoundError: No module named 'sounddevice'";
		assert.equal(
			diagnoseSounddeviceError(stderr),
			"missing-module",
			"ModuleNotFoundError for sounddevice should be 'missing-module', not 'missing-portaudio'",
		);
	});

	it("'No module named sounddevice' variant returns missing-module", () => {
		const stderr = "ImportError: No module named sounddevice";
		assert.equal(
			diagnoseSounddeviceError(stderr),
			"missing-module",
			"'No module' substring should return missing-module",
		);
	});

	it("actual portaudio error returns missing-portaudio", () => {
		const stderr = "OSError: PortAudio library not found";
		assert.equal(
			diagnoseSounddeviceError(stderr),
			"missing-portaudio",
			"PortAudio library error should return missing-portaudio",
		);
	});

	it("lowercase portaudio error returns missing-portaudio", () => {
		const stderr =
			"OSError: libportaudio.so.2: cannot open shared object file: No such file or directory";
		assert.equal(
			diagnoseSounddeviceError(stderr),
			"missing-portaudio",
			"lowercase portaudio error should return missing-portaudio",
		);
	});

	it("unrelated error returns unknown", () => {
		const stderr = "SyntaxError: invalid syntax";
		assert.equal(
			diagnoseSounddeviceError(stderr),
			"unknown",
			"unrelated error should return unknown",
		);
	});

	it("empty stderr returns unknown", () => {
		assert.equal(
			diagnoseSounddeviceError(""),
			"unknown",
			"empty stderr should return unknown",
		);
	});
});

describe("ensureVoiceVenv", () => {
	it("returns true when venv already exists", () => {
		const notifications: string[] = [];
		const result = ensureVoiceVenv({
			notify: (msg) => notifications.push(msg),
			exists: () => true,
			execFile: (() => Buffer.from("")) as any,
		});
		assert.equal(result, true, "should return true when venv exists");
		assert.equal(notifications.length, 0, "should not notify when venv exists");
	});

	it("creates venv when missing", () => {
		const notifications: string[] = [];
		const commands: string[][] = [];
		let existsCalled = false;

		const result = ensureVoiceVenv({
			notify: (msg) => notifications.push(msg),
			exists: () => {
				existsCalled = true;
				return false;
			},
			execFile: ((cmd: string, args: string[]) => {
				commands.push([cmd, ...args]);
				return Buffer.from("");
			}) as any,
		});

		assert.equal(result, true, "should return true after venv creation");
		assert.equal(existsCalled, true, "should check if venv exists");
		assert.equal(commands.length, 2, "should run 2 commands (venv + pip)");
		assert.equal(commands[0][0], "python3", "first command is python3");
		assert.ok(
			commands[0].includes("-m") && commands[0].includes("venv"),
			"first command creates venv",
		);
		assert.ok(commands[1][0].endsWith("bin/pip"), "second command is pip");
		assert.ok(commands[1].includes("sounddevice"), "pip installs sounddevice");
		assert.ok(commands[1].includes("requests"), "pip installs requests");
		assert.ok(
			notifications[0].includes("one-time setup"),
			"notifies about one-time setup",
		);
	});

	it("returns false and notifies on failure", () => {
		const notifications: Array<{ msg: string; level: string }> = [];

		const result = ensureVoiceVenv({
			notify: (msg, level) => notifications.push({ msg, level }),
			exists: () => false,
			execFile: (() => {
				throw new Error("externally-managed-environment");
			}) as any,
		});

		assert.equal(result, false, "should return false on failure");
		const errorNotif = notifications.find((n) => n.level === "error");
		assert.ok(errorNotif !== undefined, "should emit error notification");
		assert.ok(
			errorNotif!.msg.includes("python3 -m venv"),
			"error message should suggest manual venv creation",
		);
	});
});
