/**
 * @pulse-os/openclaw-memory — PULSE persistent memory plugin for OpenClaw.
 *
 * 12-source brain search, cross-agent knowledge sharing, and automatic
 * learning from every session. Local-first, zero cloud dependency.
 *
 * Tools: memory_search, memory_store, memory_get
 * Hooks: session_start, before_prompt_build, agent_end, session_end
 */

import { PulseClient } from "./client.js";
import { PulseSidecar } from "./sidecar.js";
import { createMemorySearchTool } from "./tools/memory-search.js";
import { createMemoryStoreTool } from "./tools/memory-store.js";
import { createMemoryGetTool } from "./tools/memory-get.js";
import { createAutoRecallHandler } from "./hooks/auto-recall.js";
import { createAutoCaptureHandler } from "./hooks/auto-capture.js";

interface PluginConfig {
  baseUrl?: string;
  autoRecall?: boolean;
  autoCapture?: boolean;
  maxResults?: number;
  maxContextTokens?: number;
  agentName?: string;
}

export default {
  id: "memory-pulse",
  name: "Memory (PULSE)",
  kind: "memory" as const,

  register(api: any) {
    const config: PluginConfig = api.pluginConfig || {};

    // Validate baseUrl — reject non-localhost to prevent SSRF
    let baseUrl: string;
    try {
      baseUrl = config.baseUrl || "http://127.0.0.1:8000";
      // PulseClient constructor validates localhost-only
    } catch (e: any) {
      api.logger.error(`[PULSE] Invalid baseUrl: ${e.message}. Using default.`);
      baseUrl = "http://127.0.0.1:8000";
    }

    const client = new PulseClient(baseUrl);
    const sidecar = new PulseSidecar(api.logger);

    // --- Session lifecycle ---

    api.on("session_start", async () => {
      const healthy = await client.healthCheck();
      if (healthy) {
        api.logger.info("[PULSE] Brain connected.");
        return;
      }

      api.logger.info("[PULSE] API not running, starting sidecar...");
      await sidecar.start();

      // Plumb session token from sidecar to client for auth
      if (sidecar.sessionToken) {
        client.setSessionToken(sidecar.sessionToken);
      }

      // Poll until API is ready (max 10s)
      for (let i = 0; i < 10; i++) {
        await new Promise((r) => setTimeout(r, 1_000));
        if (await client.healthCheck()) {
          api.logger.info("[PULSE] Brain connected via sidecar.");
          return;
        }
      }

      api.logger.warn(
        "[PULSE] Could not connect to brain. Memory tools will retry on use."
      );
    });

    api.on("session_end", async () => {
      sidecar.stop();
      api.logger.info("[PULSE] Session ended, sidecar stopped.");
    });

    // --- Memory tools ---

    api.registerTool(
      () => [
        createMemorySearchTool(client),
        createMemoryStoreTool(client, config),
        createMemoryGetTool(client),
      ],
      { names: ["memory_search", "memory_store", "memory_get"] }
    );

    // --- Auto-recall: inject brain context before each turn ---

    if (config.autoRecall !== false) {
      const recallHandler = createAutoRecallHandler(client, {
        maxContextTokens: config.maxContextTokens || 2_000,
      });

      api.on("before_prompt_build", recallHandler, { priority: 10 });
    }

    // --- Auto-capture: save conversation after each turn ---

    if (config.autoCapture === true) {
      api.logger.info(
        "[PULSE] Auto-capture ON. Conversations saved to local brain (PII auto-redacted). Disable with autoCapture: false."
      );
      const captureHandler = createAutoCaptureHandler(client);
      api.on("agent_end", captureHandler);
    }

    // --- CLI commands ---

    api.registerCli(
      ({ program }: any) => {
        const cmd = program
          .command("pulse")
          .description("PULSE brain operations");

        cmd
          .command("status")
          .description("Check PULSE brain connection")
          .action(async () => {
            const ok = await client.healthCheck();
            console.log(ok ? "PULSE brain: online" : "PULSE brain: offline");
          });

        cmd
          .command("search <query>")
          .description("Search PULSE brain")
          .action(async (query: string) => {
            const data = await client.search(query);
            const total = data.total_results || 0;
            console.log(`Found ${total} results (${data.elapsed_ms}ms)\n`);
            for (const [source, items] of Object.entries(data.results || {})) {
              if (!Array.isArray(items) || items.length === 0) continue;
              console.log(`--- ${source} ---`);
              for (const item of items) {
                const text = typeof item === "string" ? item : item.text || "";
                if (text) console.log(`  ${text.slice(0, 200)}`);
              }
              console.log();
            }
          });

        cmd
          .command("stats")
          .description("Show brain statistics")
          .action(async () => {
            const [lessons, failures, patterns] = await Promise.all([
              client.getLessons(1),
              client.getFailures(1),
              client.getPatterns(1),
            ]);
            console.log(`Lessons:  ${lessons.count}`);
            console.log(`Failures: ${failures.count}`);
            console.log(`Patterns: ${patterns.count}`);
          });
      },
      { commands: ["pulse"] }
    );
  },
};
