/**
 * Plugin store — Svelte 5 runes.
 *
 * Discovers plugins from ~/.config/agor/plugins/ via RPC.
 * Manages command registry (for palette integration) and event bus.
 * Coordinates with plugin-host.ts for Web Worker lifecycle.
 */

import { appRpc } from './rpc.ts';
import {
  loadPlugin,
  unloadPlugin,
  unloadAllPlugins,
  setPluginRegistries,
  type PluginMeta,
} from './plugin-host.ts';

// ── Types ────────────────────────────────────────────────────────────────────

export interface PluginCommand {
  pluginId: string;
  label: string;
  callback: () => void;
}

// ── State ────────────────────────────────────────────────────────────────────

let discovered = $state<PluginMeta[]>([]);
let commands = $state<PluginCommand[]>([]);

// ── Event bus (simple pub/sub) ───────────────────────────────────────────────

type EventHandler = (data: unknown) => void;
const eventListeners = new Map<string, Set<EventHandler>>();

const pluginEventBus = {
  on(event: string, handler: EventHandler): void {
    let set = eventListeners.get(event);
    if (!set) {
      set = new Set();
      eventListeners.set(event, set);
    }
    set.add(handler);
  },
  off(event: string, handler: EventHandler): void {
    eventListeners.get(event)?.delete(handler);
  },
  emit(event: string, data: unknown): void {
    const set = eventListeners.get(event);
    if (!set) return;
    for (const handler of set) {
      try { handler(data); }
      catch (err) { console.error(`[plugin-event] ${event}:`, err); }
    }
  },
};

// ── Command registry ─────────────────────────────────────────────────────────

function addPluginCommand(pluginId: string, label: string, callback: () => void): void {
  commands = [...commands, { pluginId, label, callback }];
}

function removePluginCommands(pluginId: string): void {
  commands = commands.filter(c => c.pluginId !== pluginId);
}

// Wire up registries to plugin-host
setPluginRegistries({
  addCommand: addPluginCommand,
  removeCommands: removePluginCommands,
  eventBus: pluginEventBus,
});

// ── Public API ───────────────────────────────────────────────────────────────

/** Discover plugins from ~/.config/agor/plugins/ via RPC. */
export async function discoverPlugins(): Promise<PluginMeta[]> {
  try {
    const res = await appRpc.request['plugin.discover']({});
    discovered = res.plugins ?? [];
    return discovered;
  } catch (err) {
    console.error('[plugin-store] discover error:', err);
    discovered = [];
    return [];
  }
}

/** Load a discovered plugin by id. */
export async function loadPluginById(pluginId: string): Promise<void> {
  const meta = discovered.find(p => p.id === pluginId);
  if (!meta) throw new Error(`Plugin not found: ${pluginId}`);
  await loadPlugin(meta);
}

/** Unload a plugin by id. */
export function unloadPluginById(pluginId: string): void {
  unloadPlugin(pluginId);
  removePluginCommands(pluginId);
}

/** Load all discovered plugins. */
export async function loadAllPlugins(): Promise<void> {
  const plugins = await discoverPlugins();
  for (const meta of plugins) {
    try {
      await loadPlugin(meta);
    } catch (err) {
      console.error(`[plugin-store] Failed to load '${meta.id}':`, err);
    }
  }
}

/** Unload all plugins. */
export function unloadAll(): void {
  unloadAllPlugins();
  commands = [];
}

/** Get discovered plugins (reactive). */
export function getDiscoveredPlugins(): PluginMeta[] {
  return discovered;
}

/** Get registered commands (reactive, for palette integration). */
export function getPluginCommands(): PluginCommand[] {
  return commands;
}

/** Emit an event to all plugins listening for it. */
export function emitPluginEvent(event: string, data: unknown): void {
  pluginEventBus.emit(event, data);
}
