/**
 * Memory tools exposed to the LLM by the chat backend.
 *
 * Registered whenever a MemoryStore is enabled — that's always true
 * now: Temper if configured, otherwise the SQLite-backed
 * LocalMemoryStore. The tool schemas and names are identical across
 * backends; only the descriptions stay accurate (e.g. `memory_search`
 * is keyword on local, semantic on Temper).
 *
 * Tool naming uses `memory_*` (no plugin prefix) so they look like
 * first-class builtins next to get_current_time.
 */

import type { BuiltinToolDef } from './tool-dispatcher';
import type { MemoryStore } from './memory-store';

export type MemoryToolHandler = (input: unknown) => Promise<string>;

export interface MemoryTool {
  def: BuiltinToolDef;
  handle: MemoryToolHandler;
}

export function buildMemoryTools(store: MemoryStore): MemoryTool[] {
  if (!store.enabled) return [];
  const isLocal = store.kind === 'local';
  const searchDescription = isLocal
    ? 'Keyword search over locally-stored memory blocks and episodes (case-insensitive LIKE). Use for finding past notes / events you wrote earlier. For first-person identity/preferences prefer memory_get_block / memory_list_blocks.'
    : 'Semantic + graph search over Temper episodes (Graphiti). Use for NAMED third-party facts ("what did Alice say about X", "what was decided on the migration"). DO NOT use for first-person questions — pronouns are filtered and you will get 0 hits. For first-person, use memory_get_block / memory_list_blocks.';
  const episodeDescription = isLocal
    ? 'Append a short event-shaped fact to the local memory log (one row per call). Searchable via memory_search keyword match. Use for "Alice prefers Postgres", "user decided to ship X on May 20".'
    : 'Write an event-shaped fact to long-term memory (a Temper episode). Use for "Alice prefers Postgres", "user decided to ship X on May 20", external-world facts with subject/object structure. Extraction runs async. ONE discrete fact per call — do NOT dump transcripts.';
  return [
    {
      def: {
        name: 'memory_get_block',
        description:
          'Read ONE memory block by key. Use this for first-person queries ("my name", "what do you call me", "my preferences"). Returns the JSON value or "(not found)".',
        input_schema: {
          type: 'object',
          properties: {
            key: { type: 'string', description: 'Block key, e.g. forge_user_name / preferences.theme / persona.nickname' },
            scope: { type: 'string', enum: ['own', 'global'], description: 'Default: own (falls back to global server-side).' },
          },
          required: ['key'],
        },
      },
      handle: async (input) => {
        const { key, scope } = input as { key: string; scope?: 'own' | 'global' };
        const b = await store.getBlock(key, scope ?? 'own');
        if (!b) return '(not found)';
        return typeof b.value === 'string' ? b.value : JSON.stringify(b.value);
      },
    },
    {
      def: {
        name: 'memory_list_blocks',
        description:
          'List memory block keys. Use when the user asks "what do you know about me" / "what have you remembered" or you need to discover what is stored. Returns key + short value preview.',
        input_schema: {
          type: 'object',
          properties: {
            pinned_only: { type: 'boolean', description: 'If true, only pinned (auto-injected) blocks. Default false.' },
            scope: { type: 'string', enum: ['own', 'global', 'both'], description: 'Default: both.' },
          },
        },
      },
      handle: async (input) => {
        const { pinned_only, scope } = input as {
          pinned_only?: boolean;
          scope?: 'own' | 'global' | 'both';
        };
        const blocks = await store.listBlocks({
          pinned: pinned_only,
          scope: scope ?? 'both',
        });
        if (!blocks.length) return '(no blocks)';
        return blocks
          .map((b) => {
            const v = typeof b.value === 'string' ? b.value : JSON.stringify(b.value);
            const short = v.length > 120 ? v.slice(0, 120) + '…' : v;
            return `- ${b.key}: ${short}`;
          })
          .join('\n');
      },
    },
    {
      def: {
        name: 'memory_search',
        description: searchDescription,
        input_schema: {
          type: 'object',
          properties: {
            query: { type: 'string', description: 'Free-text query' },
            limit: { type: 'number', description: 'Max hits to return (1-15). Default 6.' },
          },
          required: ['query'],
        },
      },
      handle: async (input) => {
        const { query, limit } = input as { query: string; limit?: number };
        const hits = await store.search(query, Math.min(15, Math.max(1, limit ?? 6)));
        if (!hits.length) return '(no hits)';
        return hits
          .filter((h) => h.fact && h.invalid_at == null)
          .map((h) => `- ${h.fact}${h.score != null ? ` (score=${h.score.toFixed(2)})` : ''}`)
          .join('\n') || '(no current facts)';
      },
    },
    {
      def: {
        name: 'memory_remember_block',
        description:
          'Store a first-person fact about the user as a memory block (key/value). Use for preferences, identity, current focus, routines — anything the user asserts about themselves. Block values REPLACE on write. Pin=true to inject into every future system prompt.',
        input_schema: {
          type: 'object',
          properties: {
            key: { type: 'string', description: 'Dotted key, e.g. preferences.language / persona.role / state.current_project' },
            value: { description: 'String or JSON object — the value to store verbatim.' },
            pinned: { type: 'boolean', description: 'If true, auto-injected into every future prompt. Use for stable identity / preferences only.' },
            description: { type: 'string', description: 'Optional human-readable note about why this block exists.' },
          },
          required: ['key', 'value'],
        },
      },
      handle: async (input) => {
        const { key, value, pinned, description } = input as {
          key: string;
          value: unknown;
          pinned?: boolean;
          description?: string;
        };
        const ok = await store.putBlock(key, value, { pinned, description });
        return ok ? `saved block: ${key}${pinned ? ' (pinned)' : ''}` : 'block save failed';
      },
    },
    {
      def: {
        name: 'memory_remember_event',
        description: episodeDescription,
        input_schema: {
          type: 'object',
          properties: {
            content: { type: 'string', description: 'Short statement of the fact, with explicit subject (name them, do not say "I" / "me").' },
            tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags like "preference" / "decision".' },
          },
          required: ['content'],
        },
      },
      handle: async (input) => {
        const { content, tags } = input as { content: string; tags?: string[] };
        const ok = await store.writeEpisode({ content, tags, source_type: 'text' });
        return ok ? 'event recorded' : 'event write failed';
      },
    },
  ];
}
