/**
 * LearnedMemoryStore - Learned memory storage
 * DD §11.3. INV-2: single writer; outbox model.
 *
 * @module packages/runtime/src/knowledge/LearnedMemoryStore
 */

import { existsSync, mkdirSync } from 'fs'
import { join } from 'path'
import { Database } from 'bun:sqlite'

export interface MemoryEntry {
  id: string
  memory_type: 'project_rule' | 'toolchain_rule' | 'skill_update' | 'debug_experience'
  summary: string
  content: string
  source_entity_type?: string
  source_entity_id?: string
  status: 'candidate' | 'promoted' | 'archived' | 'rejected'
  created_at: string
  updated_at: string
  metadata_json?: string
}

export class LearnedMemoryStore {
  private db: Database | null = null
  private db_path: string

  constructor(project_root: string) {
    this.db_path = join(project_root, '.air', 'local', 'learned-memory.db')
  }

  open(): void {
    const dir = join(this.db_path, '..')
    if (!existsSync(dir)) mkdirSync(dir, { recursive: true })

    this.db = new Database(this.db_path)
    this.db.exec('PRAGMA journal_mode = WAL')
    this.db.exec('PRAGMA synchronous = NORMAL')
    this.db.exec('PRAGMA foreign_keys = OFF')
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS learned_memories (
        id TEXT PRIMARY KEY,
        memory_type TEXT NOT NULL,
        summary TEXT NOT NULL,
        content TEXT NOT NULL,
        source_entity_type TEXT,
        source_entity_id TEXT,
        status TEXT DEFAULT 'candidate',
        created_at TEXT NOT NULL,
        updated_at TEXT NOT NULL,
        metadata_json TEXT
      );
      CREATE INDEX IF NOT EXISTS idx_memory_type ON learned_memories(memory_type);
      CREATE INDEX IF NOT EXISTS idx_memory_status ON learned_memories(status);
    `)
  }

  /**
   * Insert a memory entry.
   * INV-2: External write first, then emit memory.promoted via outbox.
   */
  insert(entry: MemoryEntry): void {
    if (!this.db) throw new Error('Store not opened')
    const stmt = this.db.prepare(`
      INSERT INTO learned_memories (id, memory_type, summary, content, source_entity_type, source_entity_id, status, created_at, updated_at, metadata_json)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `)
    stmt.run(entry.id, entry.memory_type, entry.summary, entry.content, entry.source_entity_type, entry.source_entity_id, entry.status, entry.created_at, entry.updated_at, entry.metadata_json)
  }

  lookup_by_type(memory_type: string): MemoryEntry[] {
    if (!this.db) return []
    return this.db.prepare('SELECT * FROM learned_memories WHERE memory_type = ? AND status != ? ORDER BY created_at DESC').all(memory_type, 'archived') as MemoryEntry[]
  }

  update_status(id: string, status: 'candidate' | 'promoted' | 'archived' | 'rejected'): void {
    if (!this.db) return
    const updated_at = new Date().toISOString()
    this.db.prepare('UPDATE learned_memories SET status = ?, updated_at = ? WHERE id = ?').run(status, updated_at, id)
  }

  scan_stale(days_stale: number = 90): MemoryEntry[] {
    if (!this.db) return []
    const cutoff = new Date(Date.now() - days_stale * 86400000).toISOString()
    return this.db.prepare('SELECT * FROM learned_memories WHERE status = ? AND updated_at < ?').all('promoted', cutoff) as MemoryEntry[]
  }
}
