// SQLite database client (sql.js — pure WASM, no native deps)
import initSqlJs, { type Database, type BindParams } from "sql.js";
import { createTables } from "./schema.js";

let db: Database | null = null;
let initPromise: Promise<Database> | null = null;

/** Initialize the database (call once at startup). */
export async function initDb(dbPath?: string): Promise<Database> {
  if (db) return db;
  if (initPromise) return initPromise;

  initPromise = (async () => {
    const SQL = await initSqlJs();
    const path = dbPath || process.env.DATABASE_URL || ":memory:";

    // Try to load existing database from file
    let buffer: ArrayLike<number> | undefined;
    if (path !== ":memory:") {
      try {
        const fs = await import("node:fs/promises");
        const data = await fs.readFile(path);
        buffer = new Uint8Array(data);
      } catch {
        // File doesn't exist yet — start fresh
      }
    }

    db = new SQL.Database(buffer);
    db.run("PRAGMA foreign_keys = ON");
    createTables(db);
    return db;
  })();

  return initPromise;
}

/** Get the initialized database (must call initDb first). */
export function getDb(): Database {
  if (!db) throw new Error("Database not initialized. Call initDb() first.");
  return db;
}

/** Save database to disk. */
export async function saveDb(dbPath?: string): Promise<void> {
  if (!db) return;
  const path = dbPath || process.env.DATABASE_URL || "./data/app.db";
  if (path === ":memory:") return;
  const fs = await import("node:fs/promises");
  const { dirname } = await import("node:path");
  await fs.mkdir(dirname(path), { recursive: true });
  const data = db.export();
  await fs.writeFile(path, Buffer.from(data));
}

export async function closeDb(): Promise<void> {
  if (db) {
    await saveDb();
    db.close();
    db = null;
    initPromise = null;
  }
}

// Helper: run a query and return all rows as objects
export function queryAll<T = Record<string, unknown>>(sql: string, params: BindParams = []): T[] {
  const d = getDb();
  const stmt = d.prepare(sql);
  if (params) stmt.bind(params);
  const results: T[] = [];
  while (stmt.step()) {
    const row = stmt.getAsObject();
    results.push(row as unknown as T);
  }
  stmt.free();
  return results;
}

// Helper: run a query and return the first row
export function queryOne<T = Record<string, unknown>>(sql: string, params: BindParams = []): T | undefined {
  const rows = queryAll<T>(sql, params);
  return rows[0];
}

// Helper: run a mutation and return { changes, lastInsertRowid }
export function execute(sql: string, params: BindParams = []): { changes: number; lastInsertRowid: number } {
  const d = getDb();
  d.run(sql, params);
  return {
    changes: d.getRowsModified(),
    lastInsertRowid: 0,
  };
}
