// ============================================================
// infrastructure/storage/db-core.ts — sql.js 数据库核心封装
//
// 职责：
//   - 初始化 sql.js WASM 运行时 + 创建/加载 Database
//   - 执行 DDL 建表 / 迁移
//   - 提供通用 CRUD 操作（execute / query / run / get）
//   - 导出数据库为 Uint8Array（供加密落盘）
//
// 安全：
//   - 数据库完全在内存中运行，不直接操作磁盘
//   - 数据导出由 db-encrypt.ts 加密后写入磁盘
// ============================================================

import initSqlJs, { type Database, type SqlJsStatic, type SqlValue } from 'sql.js';
import { DDL_STATEMENTS, INIT_VERSION_SQL } from './schema';
import { logger } from '@/shared/utils/bus';

// ---------- 内部状态 ----------

let SQL: SqlJsStatic | null = null;
let db: Database | null = null;

// ---------- 初始化 ----------

/**
 * 初始化 sql.js 运行时（WASM 加载），仅需调用一次。
 */
async function ensureRuntime(): Promise<SqlJsStatic> {
  if (SQL) return SQL;
  try {
    SQL = await initSqlJs({
      locateFile: (file: string) => {
        const url = import.meta.env.DEV ? `/${file}` : `./${file}`;
        console.log(`[storage] 加载 sql.js WASM：${url}`);
        return url;
      },
    });
    console.log('[storage] sql.js 运行时初始化成功');
    return SQL;
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error(`[storage] sql.js WASM 加载失败：${msg}`);
    logger.error('storage', `sql.js WASM 加载失败：${msg}`, err instanceof Error ? err : undefined);
    throw err;
  }
}

/**
 * 打开或创建数据库。
 *
 * @param encryptedData - 从磁盘读取的加密数据（由 db-encrypt.ts 解密后提供），
 *                        未提供则创建空库。
 * @returns true 表示成功
 */
export async function openDatabase(encryptedData?: Uint8Array): Promise<boolean> {
  try {
    const sql = await ensureRuntime();
    db = new sql.Database(encryptedData ?? undefined);

    // 执行建表 DDL
    db.run('PRAGMA journal_mode=OFF');
    db.run('PRAGMA synchronous=0');
    for (const stmt of DDL_STATEMENTS) {
      db.run(stmt);
    }
    db.run(INIT_VERSION_SQL);

    const size = (db.export()?.byteLength ?? 0);
    console.log(`[storage] 数据库已打开（${encryptedData ? '从磁盘恢复' : '新建'}），${DDL_STATEMENTS.length} 条 DDL，${size} bytes`);
    logger.info('storage', `数据库已打开（${encryptedData ? '从磁盘恢复' : '新建'}）`);
    return true;
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    console.error(`[storage] 数据库打开失败：${msg}`);
    logger.error('storage', `数据库打开失败：${msg}`, err instanceof Error ? err : undefined);
    return false;
  }
}

// ---------- 导出 ----------

/**
 * 导出当前数据库为 Uint8Array（供加密后写入磁盘）。
 */
export function exportDatabase(): Uint8Array | null {
  if (!db) return null;
  try {
    return db.export();
  } catch (err) {
    logger.error('storage', '数据库导出失败', err instanceof Error ? err : undefined);
    return null;
  }
}

// ---------- 关闭 ----------

export function closeDatabase(): void {
  if (db) {
    db.close();
    db = null;
    logger.info('storage', '数据库已关闭');
  }
}

export function isOpen(): boolean {
  return db !== null;
}

// ---------- 查询 API ----------

/** 确保数据库已打开 */
function guard(): Database {
  if (!db) throw new Error('数据库未初始化 — 请确认已调用 initStorage() 并等待其完成');
  return db;
}

/** 执行一条写操作 SQL（INSERT / UPDATE / DELETE），返回受影响行数 */
export function execute(sql: string, params?: SqlValue[]): number {
  try {
    guard().run(sql, params);
    return guard().getRowsModified();
  } catch (err) {
    logger.error('storage', `SQL 执行失败：${sql.substring(0, 80)}`, err instanceof Error ? err : undefined);
    throw err;
  }
}

/** 查询多条记录 */
export function queryAll<T = Record<string, unknown>>(sql: string, params?: SqlValue[]): T[] {
  try {
    const stmt = guard().prepare(sql);
    if (params) stmt.bind(params);
    const rows: T[] = [];
    while (stmt.step()) {
      rows.push(stmt.getAsObject() as T);
    }
    stmt.free();
    return rows;
  } catch (err) {
    logger.error('storage', `SQL 查询失败：${sql.substring(0, 80)}`, err instanceof Error ? err : undefined);
    return [];
  }
}

/** 查询单条记录 */
export function queryOne<T = Record<string, unknown>>(sql: string, params?: SqlValue[]): T | null {
  try {
    const stmt = guard().prepare(sql);
    if (params) stmt.bind(params);
    let row: T | null = null;
    if (stmt.step()) {
      row = stmt.getAsObject() as T;
    }
    stmt.free();
    return row;
  } catch (err) {
    logger.error('storage', `SQL 查询失败：${sql.substring(0, 80)}`, err instanceof Error ? err : undefined);
    return null;
  }
}

/** 执行多条 SQL（事务包裹） */
export function executeBatch(statements: string[]): void {
  const d = guard();
  try {
    d.run('BEGIN');
    for (const stmt of statements) {
      d.run(stmt);
    }
    d.run('COMMIT');
  } catch (err) {
    try { d.run('ROLLBACK'); } catch { /* ignore */ }
    throw err;
  }
}
