/**
 * OpenCode Lattice Plugin — Passive Data Capture
 *
 * A lightweight plugin that passively captures chat interactions.
 * The agent is unaware — data flows silently to store.db via `lattice ingest`.
 *
 * Design Notes:
 * - Only uses stable `chat.message` hook (no experimental APIs)
 * - Compresses tool output at capture time (Layer 0)
 * - Tool outputs are NOT stored — only status and errors
 * - spawn is async (non-blocking) — capture never causes UI jank
 * - Silent failure: data loss is acceptable, agent interruption is not
 * - System 1 injection and System 2 search are handled by the MCP server
 *
 * Reference: RFC-002-R1 §4.2 Plugin Interface (OpenCode)
 */

import type { Plugin } from "@opencode-ai/plugin"
import type { Part } from "@opencode-ai/sdk"
import { spawn } from "child_process"

interface Event {
  type: "user" | "assistant" | "reasoning" | "tool"
  content: string
  tool_input?: string
  tool_status?: string
  tool_error?: string
}

/**
 * Summarize tool input for storage.
 * Extracts file paths, commands, or other key identifiers.
 */
function summarize_input(tool: string, input: unknown): string {
  if (!input) return ""
  if (typeof input === "string") return input.slice(0, 100)

  const inp = input as Record<string, unknown>

  // Known tool input fields
  if (tool === "read" || tool === "edit" || tool === "write") {
    return String(inp.file_path || inp.path || "").slice(0, 100)
  }
  if (tool === "bash") {
    return String(inp.command || "").slice(0, 100)
  }
  if (tool === "glob" || tool === "grep") {
    return String(inp.pattern || "").slice(0, 100)
  }

  // Generic fallback
  for (const key of ["file_path", "path", "command", "query", "pattern", "url"]) {
    if (inp[key]) return String(inp[key]).slice(0, 100)
  }

  return ""
}

const plugin: Plugin = async () => {
  return {
    "chat.message": async (_input, output) => {
      // Extract and compress events from parts
      const events: Event[] = (output.parts || [])
        .map((part: Part): Event | null => {
          // Text content (user/assistant message)
          if (part.type === "text") {
            return {
              type: output.message.role === "user" ? "user" : "assistant",
              content: (part as { text: string }).text || "",
            }
          }

          // Reasoning content (internal monologue)
          if (part.type === "reasoning") {
            return {
              type: "reasoning",
              content: (part as { text: string }).text || "",
            }
          }

          // Tool call - compressed!
          if (part.type === "tool") {
            const p = part as {
              tool?: string
              state?: {
                input?: unknown
                status?: string
                output?: string
              }
            }
            const toolName = p.tool || "unknown"
            const state = p.state || {}

            return {
              type: "tool",
              content: toolName,
              tool_input: summarize_input(toolName, state.input),
              tool_status: state.status,
              tool_error:
                state.status === "error"
                  ? (state.output || "").slice(0, 500)
                  : undefined,
            }
          }

          return null
        })
        .filter((e): e is Event => e !== null && e.content.length > 0)

      if (events.length === 0) return

      const payload = JSON.stringify({
        session_id: _input.sessionID,
        events,
      })

      // Spawn lattice ingest and write payload to stdin
      const child = spawn("lattice", ["ingest", "--stdin"], {
        stdio: ["pipe", "ignore", "ignore"],
      })
      child.stdin?.write(payload)
      child.stdin?.end()
      // Prevent unhandled ENOENT error if 'lattice' is not in PATH
      child.on("error", () => {})
      // Silent failure - no await, no error handling
    },
  }
}

export default plugin