/**
 * Pi Done Notify Extension
 *
 * Sends a native terminal notification when Pi finishes a prompt
 * and is waiting for input. Optionally plays a sound.
 */

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";

function windowsToastScript(title: string, body: string): string {
  const type = "Windows.UI.Notifications";
  const mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`;
  const template = `[${type}.ToastTemplateType]::ToastText01`;
  const toast = `[${type}.ToastNotification]::new($xml)`;
  return [
    `${mgr} > $null`,
    `$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`,
    `$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`,
    `[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`,
  ].join("; ");
}

function notifyOSC777(title: string, body: string): void {
  process.stdout.write(`\x1b]777;notify;${title};${body}\x07`);
}

function notifyOSC99(title: string, body: string): void {
  process.stdout.write(`\x1b]99;i=1:d=1;${title}\x1b\\`);
  process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`);
}

const DEFAULT_DURATION_MS = 5000;

function notifyWindows(title: string, body: string): void {
  const { execFile } = require("child_process");
  execFile("powershell.exe", [
    "-NoProfile",
    "-Command",
    windowsToastScript(title, body),
  ]);
}

function notifyDesktop(
  title: string,
  body: string,
  iconPath: string,
  durationMs = DEFAULT_DURATION_MS
): void {
  const { execFile } = require("child_process");
  execFile(
    "notify-send",
    ["-t", `${durationMs}`, "-i", iconPath, title, body],
    (error: Error | null) => {
      if (error) {
        notifyOSC777(title, body);
      }
    }
  );
}

const ICON_PATH = "/home/thomasgl/.pi/agent/extensions/assets/pi-logo.svg";

function notify(title: string, body: string): void {
  if (process.platform === "linux") {
    notifyDesktop(title, body, ICON_PATH);
    return;
  }
  if (process.env.WT_SESSION) {
    notifyWindows(title, body);
  } else if (process.env.KITTY_WINDOW_ID) {
    notifyOSC99(title, body);
  } else {
    notifyOSC777(title, body);
  }
}

// Sound playback
function playSound(soundPath?: string): void {
  const { execFile } = require("child_process");

  if (process.platform === "darwin") {
    // macOS: use afplay with system sound or custom path
    const sound = soundPath || "/System/Library/Sounds/Glass.aiff";
    execFile("afplay", [sound], (error: Error | null) => {
      if (error) console.error("Failed to play sound:", error);
    });
  } else if (process.platform === "linux") {
    // Linux: try paplay (PulseAudio), then pw-play (PipeWire), then aplay (ALSA)
    const sound =
      soundPath || "/usr/share/sounds/freedesktop/stereo/complete.oga";
    execFile("paplay", [sound], (error: Error | null) => {
      if (error) {
        execFile("pw-play", [sound], (error2: Error | null) => {
          if (error2) {
            execFile("aplay", [sound]);
          }
        });
      }
    });
  } else if (process.platform === "win32") {
    // Windows: use PowerShell to play system sound
    const sound =
      soundPath || "C:\\Windows\\Media\\Windows Notify System Generic.wav";
    execFile("powershell.exe", [
      "-NoProfile",
      "-Command",
      `(New-Object Media.SoundPlayer '${sound}').PlaySync()`,
    ]);
  }
}

// Settings state
interface NotifySettings {
  soundEnabled: boolean;
  soundPath?: string;
}

const CUSTOM_TYPE = "pi-done-notify-settings";

export default function (pi: ExtensionAPI) {
  let settings: NotifySettings = {
    soundEnabled: true,
  };

  // Restore settings from session
  pi.on("session_start", async (_event, ctx) => {
    for (const entry of ctx.sessionManager.getEntries()) {
      if (entry.type === "custom" && entry.customType === CUSTOM_TYPE) {
        settings = entry.data as NotifySettings;
      }
    }
  });

  // Register /notify command
  pi.registerCommand("notify", {
    description: "Configure notification settings (sound on/off, or set sound path)",
    handler: async (args, ctx) => {
      const arg = args?.trim().toLowerCase();

      if (!arg || arg === "status") {
        ctx.ui.notify(
          `Sound: ${settings.soundEnabled ? "on" : "off"}${settings.soundPath ? ` (${settings.soundPath})` : ""}`,
          "info"
        );
        return;
      }

      if (arg === "on") {
        settings.soundEnabled = true;
        pi.appendEntry(CUSTOM_TYPE, settings);
        ctx.ui.notify("Notification sound enabled", "success");
        return;
      }

      if (arg === "off") {
        settings.soundEnabled = false;
        pi.appendEntry(CUSTOM_TYPE, settings);
        ctx.ui.notify("Notification sound disabled", "info");
        return;
      }

      // Treat as a sound path
      settings.soundPath = args?.trim() || undefined;
      pi.appendEntry(CUSTOM_TYPE, settings);
      ctx.ui.notify(`Sound path set to: ${settings.soundPath}`, "success");
    },
  });

  // Notify on agent end
  pi.on("agent_end", async (_event, ctx) => {
    if (!ctx.hasUI) return;

    notify("Pi", "Done. Ready for input.");

    if (settings.soundEnabled) {
      playSound(settings.soundPath);
    }
  });
}
