/**
 * Shared notification utilities for bundled extensions
 *
 * Provides cross-platform beep, speech, and bring-to-front helpers.
 * macOS features are fully supported; Linux/others gracefully degrade.
 */
import * as child_process from "node:child_process";
import * as fsPromises from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { promisify } from "node:util";

const execAsync = promisify(child_process.exec);
export const BEEP_SOUNDS = [
	"Tink",
	"Basso",
	"Blow",
	"Bottle",
	"Frog",
	"Funk",
	"Glass",
	"Hero",
	"Morse",
	"Ping",
	"Pop",
	"Purr",
	"Sosumi",
	"Submarine",
];
export const SAY_MESSAGES = [
	"Task completed",
	"Done",
	"Finished",
	"Ready",
	"All done",
	"Complete",
	"Task completed in {dirname}",
	"Done in {dirname}",
	"Finished in {dirname}",
	"All done in {dirname}",
	"{session dir} needs your attention",
];
const TERMINAL_BUNDLE_IDS = {
	"com.googlecode.iterm2": "iTerm2",
	"iTerm.app": "iTerm2",
};
// ─────────────────────────────────────────────────────────────────────────────
// Platform Detection
// ─────────────────────────────────────────────────────────────────────────────
export function isMacOS() {
	return process.platform === "darwin";
}
let hasSayCommand = false;
export async function checkSayAvailable() {
	if (!isMacOS()) {
		hasSayCommand = false;
		return false;
	}
	try {
		await execAsync("which say");
		hasSayCommand = true;
		return true;
	} catch {
		hasSayCommand = false;
		return false;
	}
}
export function isSayAvailable() {
	return hasSayCommand;
}
// ─────────────────────────────────────────────────────────────────────────────
// Terminal Detection
// ─────────────────────────────────────────────────────────────────────────────
export async function detectTerminalInfo() {
	const info = {};
	if (!isMacOS()) return info;
	try {
		info.terminalPid = process.ppid;
		info.terminalApp = process.env.TERM_PROGRAM;
		if (!info.terminalTTY) {
			try {
				const { stdout } = await execAsync(`ps -p ${process.ppid} -o tty=`);
				const tty = stdout.trim();
				if (tty && tty !== "??") {
					info.terminalTTY = tty.startsWith("/dev/") ? tty : "/dev/" + tty;
				}
			} catch {}
		}
		if (!info.terminalTTY && info.terminalPid) {
			try {
				const { stdout } = await execAsync(
					`lsof -p ${info.terminalPid} 2>/dev/null | grep -m1 "/dev/ttys" | awk '{print $9}'`,
				);
				const tty = stdout.trim();
				if (tty?.startsWith("/dev/")) info.terminalTTY = tty;
			} catch {}
		}
		if (!info.terminalApp) {
			try {
				const { stdout } = await execAsync(
					`lsappinfo info -only bundleID ${info.terminalPid}`,
				);
				const match = stdout.match(/"CFBundleIdentifier"="([^"]+)"/);
				if (match) info.terminalApp = match[1];
			} catch {
				info.terminalApp = "com.googlecode.iterm2";
			}
		}
	} catch {}
	return info;
}
export async function isTerminalInBackground(info) {
	if (!isMacOS()) return false;
	try {
		const { stdout } = await execAsync(
			"lsappinfo front | awk '{print $1}' | xargs -I {} lsappinfo info -only bundleID {}",
		);
		const match = stdout.match(/"CFBundleIdentifier"="([^"]+)"/);
		if (!match) return false;
		const frontBundleId = match[1];
		if (info.terminalApp && !frontBundleId.includes(info.terminalApp))
			return true;
		const knownTerminals = Object.keys(TERMINAL_BUNDLE_IDS).filter((k) =>
			k.includes("."),
		);
		return !knownTerminals.some((id) => frontBundleId.includes(id));
	} catch {
		return false;
	}
}
// ─────────────────────────────────────────────────────────────────────────────
// terminal-notifier
// ─────────────────────────────────────────────────────────────────────────────
let terminalNotifierAvailable = false;
let terminalNotifierChecked = false;
const TERMINAL_NOTIFIER_PATHS = [
	"/Applications/terminal-notifier.app/Contents/MacOS/terminal-notifier",
	"/usr/local/bin/terminal-notifier",
	"/opt/homebrew/bin/terminal-notifier",
];
export async function checkTerminalNotifierAvailable() {
	if (!isMacOS() || terminalNotifierChecked) return terminalNotifierAvailable;
	try {
		await execAsync("which terminal-notifier");
		for (const p of TERMINAL_NOTIFIER_PATHS) {
			try {
				await execAsync(`test -f "${p}"`);
				terminalNotifierAvailable = true;
				break;
			} catch {}
		}
	} catch {
		terminalNotifierAvailable = false;
	}
	terminalNotifierChecked = true;
	return terminalNotifierAvailable;
}
export function isTerminalNotifierAvailable() {
	return terminalNotifierAvailable;
}
// ─────────────────────────────────────────────────────────────────────────────
// Message Helpers
// ─────────────────────────────────────────────────────────────────────────────
export function getCurrentDirName() {
	try {
		return process.cwd().split("/").pop() || "unknown";
	} catch {
		return "unknown";
	}
}
export function replaceMessageTemplates(message) {
	const dirName = getCurrentDirName();
	return message
		.replace(/{session dir}/g, dirName)
		.replace(/{dirname}/g, dirName);
}
// ─────────────────────────────────────────────────────────────────────────────
// Notification Actions
// ─────────────────────────────────────────────────────────────────────────────
export function playBeep(soundName = "Tink") {
	if (isMacOS()) {
		child_process
			.spawn("afplay", [`/System/Library/Sounds/${soundName}.aiff`], {
				detached: true,
				stdio: "ignore",
			})
			.unref();
	} else if (process.platform === "linux") {
		try {
			child_process
				.spawn("paplay", ["/usr/share/sounds/freedesktop/stereo/bell.oga"], {
					detached: true,
					stdio: "ignore",
				})
				.unref();
		} catch {
			child_process.exec("echo -e '\\a'");
		}
	} else {
		child_process.exec("echo -e '\\a'");
	}
}
export function displayOSXNotification(message, soundName, _terminalInfo) {
	if (!isMacOS()) {
		if (soundName) playBeep(soundName);
		return;
	}
	const finalMessage = replaceMessageTemplates(message);
	const terminalBundleId = "com.googlecode.iterm2";
	if (terminalNotifierAvailable) {
		const args = [
			"-message",
			finalMessage,
			"-title",
			"SF Notify",
			"-activate",
			terminalBundleId,
		];
		if (soundName) args.push("-sound", soundName);
		child_process
			.spawn("terminal-notifier", args, { detached: true, stdio: "ignore" })
			.unref();
		return;
	}
	const escapedMessage = finalMessage
		.replace(/\\/g, "\\\\")
		.replace(/"/g, '\\"');
	const terminalAppName = "iTerm2";
	let script = `tell application "${terminalAppName}" to display notification "${escapedMessage}" with title "SF Notify"`;
	if (soundName) script += ` sound name "${soundName}"`;
	child_process
		.spawn("osascript", ["-e", script], { detached: true, stdio: "ignore" })
		.unref();
}
export function speakMessage(message) {
	if (!isSayAvailable()) return;
	const finalMessage = replaceMessageTemplates(message).replace(/"/g, '\\"');
	child_process
		.spawn("say", ["-v", "Daniel", finalMessage], {
			detached: true,
			stdio: "ignore",
		})
		.unref();
}
export async function bringTerminalToFront(info) {
	if (!isMacOS()) return;
	try {
		let script;
		if (info.terminalTTY) {
			script = `tell application "iTerm2"
  repeat with w in windows
    set tabIdx to 0
    repeat with t in tabs of w
      set tabIdx to tabIdx + 1
      repeat with s in sessions of t
        if tty of s is "${info.terminalTTY}" then
          tell w to select tab tabIdx
          activate
          return
        end if
      end repeat
    end repeat
  end repeat
end tell`;
		} else {
			script = `tell application "iTerm2" to activate`;
		}
		const tmpFile = path.join(os.tmpdir(), `sf-terminal-${Date.now()}.scpt`);
		try {
			await fsPromises.writeFile(tmpFile, script, "utf8");
			await execAsync(`osascript "${tmpFile}"`);
		} finally {
			try {
				await fsPromises.unlink(tmpFile);
			} catch {}
		}
	} catch {}
}
export async function notifyOnConfirm(config, terminalInfo, options) {
	const eff = {
		beep: options?.beep ?? config.beep,
		beepSound: options?.beepSound ?? config.beepSound,
		bringToFront: options?.bringToFront ?? config.bringToFront,
		say: isSayAvailable() ? (options?.say ?? config.say) : false,
		sayMessage: options?.sayMessage ?? config.sayMessage,
	};
	const tasks = [];
	if (eff.bringToFront) tasks.push(bringTerminalToFront(terminalInfo));
	if (eff.beep) playBeep(eff.beepSound);
	if (eff.say) speakMessage(eff.sayMessage);
	await Promise.all(tasks);
}
