/**
 * A simple key-value store using Bun's SQLite support.
 * Supports string, object, boolean, and null values.
 * Values can have an optional TTL (time-to-live) in seconds.
 *
 * Exports a set of async functions for easy replacement
 * with other storage solutions in the future.
 *
 * Usage:
 * import { get, set, del, has, clear, connectDB } from './kv';
 *
 * await connectDB(); // Optional: specify a custom database file path
 * await set('myKey', 'myValue', 60); // Set a key with a value and TTL
 * const value = await get('myKey'); // Get the value by key
 * await del('myKey'); // Delete the key
 * const exists = await has('myKey'); // Check if the key exists
 * await clear(); // Clear all keys
 */

import { Database } from 'bun:sqlite';

interface KVItem {
  key: string;
  value: string;
  ttl: number | null; // Unix timestamp in milliseconds
}

let db: Database | null = null;

export function connectDB(dbPath: string = process.env.STAR_KITTEN_KV_DB_PATH || ':memory:') {
  db = new Database(dbPath);
  db.run(
    `CREATE TABLE IF NOT EXISTS kvstore (
      key TEXT PRIMARY KEY,
      value TEXT,
      ttl INTEGER,
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      UNIQUE(key)
    )`,
  );
}

export function getDB() {
  if (!db) {
    connectDB();
  }
  return db!;
}

export function get<T>(key: string): T | undefined {
  const db = getDB();
  const query = db.prepare(`SELECT value, ttl FROM kvstore WHERE key = ?`);
  const row = query.get(key) as KVItem | undefined;
  if (!row) return undefined;

  if (row.ttl && Date.now() > row.ttl) {
    del(key);
    return undefined;
  }

  try {
    return JSON.parse(row.value) as T;
  } catch (error) {
    return row.value as unknown as T;
  }
}

export function del(key: string | string[]): number {
  try {
    if (typeof key === 'string') {
      const result = getDB().run(`DELETE FROM kvstore WHERE key = ?`, [key]);
      return result.changes;
    } else {
      const keys = key.map(() => '?').join(', ');
      const query = `DELETE FROM kvstore WHERE key IN (${keys})`;
      const result = getDB().run(query, key);
      return result.changes;
    }
  } catch (error) {
    return 0;
  }
}

export function set<T>(key: string, value: T, ttlInSeconds: number | null = null): boolean {
  const ttl = ttlInSeconds ? Date.now() + ttlInSeconds * 1000 : null;
  try {
    const stmt = getDB().run(`INSERT OR REPLACE INTO kvstore VALUES (?, ?, ?, ?)`, [
      key,
      JSON.stringify(value),
      ttl,
      new Date().toISOString(),
    ]);
    return true;
  } catch (error) {
    return false;
  }
}

export function has(key: string): boolean {
  const db = getDB();
  const query = db.prepare(`SELECT ttl FROM kvstore WHERE key = ?`);
  const row = query.get(key) as { ttl?: number } | undefined;
  if (!row) return false;
  if (row.ttl && Date.now() > row.ttl) {
    del(key);
    return false;
  }
  return true;
}

export function purgeOlderThan(ageInSeconds: number): number {
  const cutoff = Date.now() - ageInSeconds * 1000;
  const result = getDB().run(`DELETE FROM kvstore WHERE created_at < ?`, [new Date(cutoff).toISOString()]);
  return result.changes;
}

export function clear(): void {
  getDB().run(`DELETE FROM kvstore`);
}

export default {
  get,
  set,
  delete: del,
  del,
  has,
  clear,
};

export const asyncKV = {
  get: <T>(key: string): Promise<T | undefined> => Promise.resolve(get<T>(key)),
  set: <T>(key: string, value: T, ttlInSeconds: number | null = null): Promise<boolean> =>
    Promise.resolve(set<T>(key, value, ttlInSeconds)),
  delete: (key: string | string[]): Promise<number> => Promise.resolve(del(key)),
  del: (key: string | string[]): Promise<number> => Promise.resolve(del(key)),
  has: (key: string): Promise<boolean> => Promise.resolve(has(key)),
  clear: (): Promise<void> => Promise.resolve(clear()),
};
