import { spawn } from "child_process";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync } from "node:fs";

const here = dirname(fileURLToPath(import.meta.url));

const ASAR_TOKEN = "app.asar";
const ASAR_UNPACKED_TOKEN = "app.asar.unpacked";

function unpacked(p) {
	if (!p || !p.includes(ASAR_TOKEN)) return p;
	if (p.includes(ASAR_UNPACKED_TOKEN)) return p;
	return p.replace(ASAR_TOKEN, ASAR_UNPACKED_TOKEN);
}

const exeName = { win32: "sox.exe", darwin: "soxmac", linux: "soxlinux" }[
	process.platform
];

let soxPath = process.argv[2] || join(here, "..", "binaries", exeName);
soxPath = unpacked(soxPath);

if (!existsSync(soxPath)) {
	console.error(`SoX not found: ${soxPath}`);
	process.exit(1);
}

export function listAudioDevices() {
	return new Promise((resolve, reject) => {
		if (process.platform !== "win32") {
			// On macOS and Linux, SoX uses default device
			resolve([{ id: "default", name: "Default Audio Input" }]);
			return;
		}

		// Windows: Use PowerShell to enumerate capture endpoints so names match Sound settings
		const psCommand = `
$devices = @()
$defaultName = $null

# Try to read the current default recording device
try {
  $defaultKey = 'HKCU:\\SOFTWARE\\Microsoft\\Multimedia\\Sound Mapper'
  if (Test-Path $defaultKey) {
    $defaultName = (Get-ItemProperty -Path $defaultKey -ErrorAction SilentlyContinue).Record
  }
} catch {}

# First pass: registry-backed capture endpoints (matches Sound control panel)
try {
  $captureRoot = 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture'
  if (Test-Path $captureRoot) {
    foreach ($dev in Get-ChildItem $captureRoot) {
      $props = Get-ItemProperty $dev.PSPath
      $name = $props.FriendlyName
      $state = $props.DeviceState

      # Only include active/available devices with names
      if ([string]::IsNullOrWhiteSpace($name)) { continue }
      if (($state -band 1) -ne 1) { continue }

      $displayName = $name
      if ($defaultName -and $name -eq $defaultName) {
        $displayName = "$name (Default)"
      }

      $devices += [PSCustomObject]@{
        name = $displayName
        id   = $name  # SoX expects the friendly name
      }
    }
  }
} catch {}

# Fallback: PnP endpoints filtered to likely inputs
if (-not $devices.Count) {
  try {
    $pnp = Get-CimInstance -ClassName Win32_PnPEntity -Filter "PNPClass='AudioEndpoint'" -ErrorAction Stop
    foreach ($d in $pnp) {
      if (-not $d.Name) { continue }
      if ($d.Name -notmatch '(?i)microphone|mic|input|line in|array|webcam|stereo mix') { continue }
      $devices += [PSCustomObject]@{ name = $d.Name; id = $d.Name }
    }
  } catch {}
}

$devices | ConvertTo-Json -Depth 3
		`;
		const proc = spawn("powershell", ["-NoProfile", "-NonInteractive", "-Command", psCommand], {
			encoding: "utf8",
			windowsHide: true,
		});

		let output = "";
		let errorOutput = "";

		proc.stdout?.on("data", (data) => {
			output += data.toString();
		});

		proc.stderr?.on("data", (data) => {
			errorOutput += data.toString();
		});

		proc.on("close", (code) => {
			const devices = [];
			
			try {
				// Try to parse PowerShell JSON output
				if (output.trim()) {
					const psDevices = JSON.parse(output);
					const deviceArray = Array.isArray(psDevices) ? psDevices : [psDevices];
					
					deviceArray.forEach((device, index) => {
						const name = device?.name || device?.Name;
						const id = device?.id || device?.Id || device?.ID;
						if (name) {
							devices.push({
								id: (id ?? name).toString(),
								name,
							});
						}
					});
				}
			} catch (e) {
				// PowerShell failed, try alternative method
			}

			// If PowerShell didn't work, try using Get-AudioDevice module if available
			if (devices.length === 0) {
				// Provide numbered device options for manual selection
				// SoX on Windows typically uses device numbers 0-9
				for (let i = 0; i <= 5; i++) {
					devices.push({
						id: i.toString(),
						name: `Audio Input Device ${i}${i === 0 ? ' (Default)' : ''}`,
					});
				}
				
				console.log("\nNote: Unable to query actual device names.");
				console.log("Showing generic device numbers (0-5).");
				console.log("You may need to test each to find your microphone.\n");
			}

			resolve(devices);
		});

		proc.on("error", (err) => {
			// If PowerShell is not available, provide default options
			const devices = [];
			for (let i = 0; i <= 5; i++) {
				devices.push({
					id: i.toString(),
					name: `Audio Input Device ${i}${i === 0 ? ' (Default)' : ''}`,
				});
			}
			resolve(devices);
		});
	});
}

// If run directly, list devices
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, '/'))) {
	listAudioDevices()
		.then((devices) => {
			console.log("Available audio input devices:");
			devices.forEach((device) => {
				console.log(`  ${device.id}: ${device.name}`);
			});
		})
		.catch((err) => {
			console.error("Error listing devices:", err);
			process.exit(1);
		});
}
