import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { homedir } from 'node:os';
import cron from 'node-cron';
import initSqlJs from 'sql.js';
import { j as join } from '../shared/ccjk.bQ7Dh1g4.mjs';

class DecayScheduler {
  memoryTree;
  task = null;
  archiveThreshold;
  constructor(memoryTree, archiveThreshold = 0.3) {
    this.memoryTree = memoryTree;
    this.archiveThreshold = archiveThreshold;
  }
  /**
   * Start scheduled decay (user must explicitly call this)
   */
  start(schedule = "0 2 * * *") {
    if (this.task) {
      console.warn("[CCJK Context] Decay scheduler already running");
      return;
    }
    if (!cron.validate(schedule)) {
      throw new Error(`Invalid cron expression: ${schedule}`);
    }
    console.log(`[CCJK Context] Starting decay scheduler: ${schedule}`);
    console.log("[CCJK Context] This will run in the background. Use .stop() to disable.");
    this.task = cron.schedule(schedule, async () => {
      console.log("[CCJK Context] Running scheduled decay...");
      try {
        const result = await this.runDecay();
        console.log(`[CCJK Context] Decayed ${result.decayed} nodes, archived ${result.archived} nodes`);
      } catch (err) {
        console.error("[CCJK Context] Decay error:", err);
      }
    });
  }
  /**
   * Stop scheduled decay
   */
  stop() {
    if (this.task) {
      this.task.stop();
      this.task = null;
      console.log("[CCJK Context] Decay scheduler stopped");
    }
  }
  /**
   * Check if scheduler is running
   */
  isRunning() {
    return this.task !== null;
  }
  /**
   * Run decay manually
   */
  async runNow() {
    return this.runDecay();
  }
  /**
   * Preview what would be decayed (dry run)
   */
  async preview() {
    const stats = await this.memoryTree.getStats();
    return {
      wouldDecay: stats.totalNodes - (stats.byPriority.P0 || 0) || 0,
      wouldArchive: stats.brownLeaves,
      stats
    };
  }
  /**
   * Internal decay execution
   */
  async runDecay() {
    const decayed = await this.memoryTree.applyDecay();
    const archived = await this.memoryTree.archiveLowConfidence(this.archiveThreshold);
    return {
      decayed,
      archived,
      details: { threshold: this.archiveThreshold }
    };
  }
}

let SQL = null;
async function initSQL() {
  if (!SQL) {
    SQL = await initSqlJs();
  }
  return SQL;
}
async function createDatabase(path) {
  const sql = await initSQL();
  if (existsSync(path)) {
    const buffer = readFileSync(path);
    return new sql.Database(buffer);
  }
  return new sql.Database();
}
function saveDatabase(db, path) {
  const data = db.export();
  writeFileSync(path, data);
}

class MemoryTree {
  db;
  dbPath;
  initialized = false;
  constructor(dbPath) {
    this.dbPath = dbPath || this.getDefaultDbPath();
    this.ensureDbDirectory();
  }
  async init() {
    if (!this.initialized) {
      this.db = await createDatabase(this.dbPath);
      this.initSchema();
      this.initialized = true;
    }
  }
  getDefaultDbPath() {
    const ccjkDir = process.env.ZCF_CONFIG_DIR || join(homedir(), ".ccjk");
    return join(ccjkDir, "memory.db");
  }
  ensureDbDirectory() {
    const dir = this.dbPath.substring(0, this.dbPath.lastIndexOf("/"));
    if (!existsSync(dir)) {
      mkdirSync(dir, { recursive: true });
    }
  }
  initSchema() {
    this.db.run(`
      CREATE TABLE IF NOT EXISTS memory_nodes (
        id TEXT PRIMARY KEY,
        content TEXT NOT NULL,
        summary TEXT NOT NULL,
        confidence REAL NOT NULL,
        priority TEXT NOT NULL,
        last_accessed INTEGER NOT NULL,
        access_count INTEGER NOT NULL,
        created_at INTEGER NOT NULL,
        metadata TEXT
      );
    `);
    this.db.run(`CREATE INDEX IF NOT EXISTS idx_confidence ON memory_nodes(confidence);`);
    this.db.run(`CREATE INDEX IF NOT EXISTS idx_priority ON memory_nodes(priority);`);
    this.db.run(`CREATE INDEX IF NOT EXISTS idx_last_accessed ON memory_nodes(last_accessed);`);
    this.save();
  }
  async addNode(node) {
    await this.init();
    const id = `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const now = Date.now();
    this.db.run(
      `INSERT INTO memory_nodes (id, content, summary, confidence, priority, last_accessed, access_count, created_at, metadata)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [id, node.content, node.summary, node.confidence, node.priority, now, 0, now, JSON.stringify(node.metadata || {})]
    );
    this.save();
    return id;
  }
  async getNode(id) {
    await this.init();
    const results = this.db.exec(`SELECT * FROM memory_nodes WHERE id = ?`, [id]);
    if (!results.length || !results[0].values.length)
      return null;
    const row = results[0].values[0];
    return this.rowToNode(results[0].columns, row);
  }
  async search(query, options = {}) {
    await this.init();
    const { limit = 10, minConfidence = 0.3, priorities = ["P0", "P1", "P2"] } = options;
    const priorityFilter = priorities.map(() => "?").join(",");
    const pattern = `%${query}%`;
    const sql = `
      SELECT *,
        (CASE
          WHEN content LIKE ? THEN 1
          WHEN summary LIKE ? THEN 2
          ELSE 3
        END) as score
      FROM memory_nodes
      WHERE (content LIKE ? OR summary LIKE ?)
        AND confidence >= ?
        AND priority IN (${priorityFilter})
      ORDER BY score, confidence DESC
      LIMIT ?
    `;
    const results = this.db.exec(sql, [pattern, pattern, pattern, pattern, minConfidence, ...priorities, limit]);
    if (!results.length || !results[0].values.length)
      return [];
    return results[0].values.map((row) => {
      const node = this.rowToNode(results[0].columns, row);
      return { ...node, score: row[results[0].columns.indexOf("score")] };
    });
  }
  async updateConfidence(id, delta) {
    await this.init();
    this.db.run(
      `UPDATE memory_nodes
       SET confidence = CASE
         WHEN confidence + ? > 1.0 THEN 1.0
         WHEN confidence + ? < 0.0 THEN 0.0
         ELSE confidence + ?
       END
       WHERE id = ?`,
      [delta, delta, delta, id]
    );
    this.save();
  }
  async recordAccess(id) {
    await this.init();
    const now = Date.now();
    this.db.run(
      `UPDATE memory_nodes
       SET last_accessed = ?,
           access_count = access_count + 1,
           confidence = CASE
             WHEN confidence + 0.05 > 1.0 THEN 1.0
             ELSE confidence + 0.05
           END
       WHERE id = ?`,
      [now, id]
    );
    this.save();
  }
  async applyDecay(decayRate = 0.01) {
    await this.init();
    const before = this.db.exec(`SELECT COUNT(*) FROM memory_nodes`);
    const countBefore = before[0]?.values[0]?.[0] || 0;
    this.db.run(
      `UPDATE memory_nodes
       SET confidence = CASE
         WHEN confidence - ? < 0.0 THEN 0.0
         ELSE confidence - ?
       END`,
      [decayRate, decayRate]
    );
    this.save();
    return countBefore;
  }
  async archiveLowConfidence(threshold = 0.3) {
    await this.init();
    const before = this.db.exec(`SELECT COUNT(*) FROM memory_nodes WHERE confidence < ?`, [threshold]);
    const count = before[0]?.values[0]?.[0] || 0;
    this.db.run(`DELETE FROM memory_nodes WHERE confidence < ?`, [threshold]);
    this.save();
    return count;
  }
  async getStats() {
    await this.init();
    const results = this.db.exec(`
      SELECT
        COUNT(*) as total,
        SUM(CASE WHEN confidence >= 0.8 THEN 1 ELSE 0 END) as green,
        SUM(CASE WHEN confidence >= 0.5 AND confidence < 0.8 THEN 1 ELSE 0 END) as yellow,
        SUM(CASE WHEN confidence >= 0.3 AND confidence < 0.5 THEN 1 ELSE 0 END) as brown,
        SUM(CASE WHEN confidence < 0.3 THEN 1 ELSE 0 END) as archived,
        AVG(confidence) as avg_confidence
      FROM memory_nodes
    `);
    const priorityResults = this.db.exec(`
      SELECT priority, COUNT(*) as count
      FROM memory_nodes
      GROUP BY priority
    `);
    const row = results[0]?.values[0] || [];
    const byPriority = {};
    if (priorityResults.length && priorityResults[0].values.length) {
      for (const pRow of priorityResults[0].values) {
        byPriority[pRow[0]] = pRow[1];
      }
    }
    return {
      totalNodes: row[0] || 0,
      greenLeaves: row[1] || 0,
      yellowLeaves: row[2] || 0,
      brownLeaves: row[3] || 0,
      archived: row[4] || 0,
      avgConfidence: row[5] || 0,
      byPriority
    };
  }
  async getAllNodes(options = {}) {
    await this.init();
    const { minConfidence = 0, priorities = ["P0", "P1", "P2"], limit } = options;
    const priorityFilter = priorities.map(() => "?").join(",");
    let sql = `
      SELECT * FROM memory_nodes
      WHERE confidence >= ?
        AND priority IN (${priorityFilter})
      ORDER BY confidence DESC, last_accessed DESC
    `;
    const params = [minConfidence, ...priorities];
    if (limit) {
      sql += " LIMIT ?";
      params.push(limit);
    }
    const results = this.db.exec(sql, params);
    if (!results.length || !results[0].values.length)
      return [];
    return results[0].values.map((row) => this.rowToNode(results[0].columns, row));
  }
  async deleteNode(id) {
    await this.init();
    this.db.run(`DELETE FROM memory_nodes WHERE id = ?`, [id]);
    this.save();
    const results = this.db.exec(`SELECT changes()`);
    return (results[0]?.values[0]?.[0] || 0) > 0;
  }
  async clear() {
    await this.init();
    this.db.run(`DELETE FROM memory_nodes`);
    this.save();
  }
  close() {
    if (this.initialized) {
      this.save();
      this.db.close();
      this.initialized = false;
    }
  }
  save() {
    if (this.initialized) {
      saveDatabase(this.db, this.dbPath);
    }
  }
  rowToNode(columns, row) {
    const obj = {};
    columns.forEach((col, i) => {
      obj[col] = row[i];
    });
    return {
      id: obj.id,
      content: obj.content,
      summary: obj.summary,
      confidence: obj.confidence,
      priority: obj.priority,
      lastAccessed: new Date(obj.last_accessed),
      accessCount: obj.access_count,
      createdAt: new Date(obj.created_at),
      metadata: obj.metadata ? JSON.parse(obj.metadata) : {}
    };
  }
}

async function contextOptStats() {
  if (process.env.CCJK_MEMORY_TREE !== "true") {
    console.error("\u274C Memory tree not enabled. Set CCJK_MEMORY_TREE=true");
    process.exit(1);
  }
  try {
    const tree = new MemoryTree();
    const stats = await tree.getStats();
    console.log("\n\u{1F4CA} Memory Tree Statistics\n");
    console.log(`Total Nodes: ${stats.totalNodes}`);
    console.log(`Average Confidence: ${stats.avgConfidence.toFixed(2)}
`);
    console.log("By Confidence:");
    console.log(`  \u{1F7E2} Green (\u22650.8):  ${stats.greenLeaves}`);
    console.log(`  \u{1F7E1} Yellow (0.5-0.8): ${stats.yellowLeaves}`);
    console.log(`  \u{1F7E4} Brown (0.3-0.5):  ${stats.brownLeaves}`);
    console.log(`  \u26AB Archived (<0.3):  ${stats.archived}
`);
    console.log("By Priority:");
    console.log(`  P0 (Critical): ${stats.byPriority.P0 || 0}`);
    console.log(`  P1 (Important): ${stats.byPriority.P1 || 0}`);
    console.log(`  P2 (Routine): ${stats.byPriority.P2 || 0}`);
    tree.close();
  } catch (err) {
    console.error("\u274C Failed to get stats:", err);
    process.exit(1);
  }
}
async function contextOptSearch(query, options = {}) {
  if (process.env.CCJK_MEMORY_TREE !== "true") {
    console.error("\u274C Memory tree not enabled. Set CCJK_MEMORY_TREE=true");
    process.exit(1);
  }
  try {
    const tree = new MemoryTree();
    const topK = parseInt(options.topK || "5");
    const results = await tree.search(query, { limit: topK });
    if (results.length === 0) {
      console.log("\n\u{1F50D} No results found\n");
      tree.close();
      return;
    }
    console.log(`
\u{1F50D} Found ${results.length} results:
`);
    results.forEach((node, i) => {
      console.log(`${i + 1}. [${node.priority}] Confidence: ${node.confidence.toFixed(2)} | Score: ${node.score.toFixed(2)}`);
      console.log(`   ${node.summary.slice(0, 100)}${node.summary.length > 100 ? "..." : ""}`);
      console.log(`   Accessed: ${node.accessCount} times | Last: ${node.lastAccessed.toISOString()}
`);
    });
    tree.close();
  } catch (err) {
    console.error("\u274C Search failed:", err);
    process.exit(1);
  }
}
async function contextOptDecay() {
  if (process.env.CCJK_MEMORY_TREE !== "true") {
    console.error("\u274C Memory tree not enabled. Set CCJK_MEMORY_TREE=true");
    process.exit(1);
  }
  try {
    const tree = new MemoryTree();
    const threshold = 0.3;
    const scheduler = new DecayScheduler(tree, threshold);
    console.log("\n\u2699\uFE0F  Running decay...\n");
    const result = await scheduler.runNow();
    console.log(`\u2705 Decayed: ${result.decayed} nodes`);
    console.log(`\u{1F4E6} Archived: ${result.archived} nodes
`);
    console.log("Details:");
    Object.entries(result.details).forEach(([priority, count]) => {
      console.log(`  ${priority}: ${count} nodes`);
    });
    tree.close();
  } catch (err) {
    console.error("\u274C Decay failed:", err);
    process.exit(1);
  }
}
async function contextOptConfig() {
  console.log("\n\u2699\uFE0F  Context Optimization Configuration\n");
  console.log(`CCJK_CONTEXT_OPTIMIZATION: ${process.env.CCJK_CONTEXT_OPTIMIZATION || "false"}`);
  console.log(`CCJK_TOOL_COMPRESSION: ${process.env.CCJK_TOOL_COMPRESSION || "true"}`);
  console.log(`CCJK_SEMANTIC_COMPRESSION: ${process.env.CCJK_SEMANTIC_COMPRESSION || "false"}`);
  console.log(`CCJK_MEMORY_TREE: ${process.env.CCJK_MEMORY_TREE || "false"}`);
  console.log(`CCJK_MAX_CONTEXT_TOKENS: ${process.env.CCJK_MAX_CONTEXT_TOKENS || "150000"}`);
  console.log(`CCJK_COMPRESSION_TIMEOUT: ${process.env.CCJK_COMPRESSION_TIMEOUT || "50"}ms
`);
  const ccjkDir = process.env.ZCF_CONFIG_DIR || join(homedir(), ".ccjk");
  const dbPath = join(ccjkDir, "memory.db");
  const dbExists = existsSync(dbPath);
  console.log(`Memory DB: ${dbPath}`);
  console.log(`DB Exists: ${dbExists ? "\u2705" : "\u274C"}
`);
}

export { contextOptConfig, contextOptDecay, contextOptSearch, contextOptStats };
