/**
 * اتصال به پایگاه داده.
 * دو حالت پشتیبانی می‌شود و در هر دو، گویش SQL دقیقاً PostgreSQL است:
 *   ۱) DATABASE_URL تنظیم شده باشد → PostgreSQL واقعی (Data Center)
 *   ۲) در غیر این صورت → PGlite (PostgreSQL روی WASM) با ذخیره‌سازی روی دیسک
 */
import fs from 'node:fs';
import path from 'node:path';
import { PGlite } from '@electric-sql/pglite';
import { drizzle as drizzlePglite } from 'drizzle-orm/pglite';
import { sql } from 'drizzle-orm';
import * as schema from './schema/index.js';
import { config, rootDir } from '../config/index.js';

export type Database = ReturnType<typeof drizzlePglite<typeof schema>>;

let client: PGlite | null = null;
let dbInstance: Database | null = null;

export function getDb(): Database {
  if (!dbInstance) throw new Error('پایگاه داده هنوز مقداردهی نشده است. ابتدا initDatabase را صدا بزنید.');
  return dbInstance;
}

export function getRawClient(): PGlite {
  if (!client) throw new Error('اتصال خام پایگاه داده در دسترس نیست.');
  return client;
}

export async function initDatabase(options: { fresh?: boolean } = {}): Promise<Database> {
  if (dbInstance && !options.fresh) return dbInstance;

  const dataDir = config.database.embeddedPath;
  if (options.fresh && fs.existsSync(dataDir)) fs.rmSync(dataDir, { recursive: true, force: true });
  fs.mkdirSync(path.dirname(dataDir), { recursive: true });

  client = new PGlite(process.env.PGLITE_MEMORY === '1' ? undefined : dataDir);
  await client.waitReady;
  dbInstance = drizzlePglite(client, { schema });

  await runMigrations(dbInstance);
  return dbInstance;
}

/**
 * اجرای مهاجرت‌های تولیدشده توسط drizzle-kit.
 * چون فایل‌ها با گویش PostgreSQL ساخته شده‌اند، همان فایل‌ها روی سرور واقعی هم
 * اجرا می‌شوند؛ بنابراین محیط توسعه و محیط عملیاتی از هم واگرا نمی‌شوند.
 */
async function runMigrations(db: Database) {
  const dir = path.join(rootDir, 'src/database/migrations');
  if (!fs.existsSync(dir)) return;
  const files = fs.readdirSync(dir).filter((f) => f.endsWith('.sql')).sort();

  await db.execute(sql`CREATE TABLE IF NOT EXISTS __migrations (name text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now())`);
  const appliedRows = await db.execute<{ name: string }>(sql`SELECT name FROM __migrations`);
  const applied = new Set((appliedRows.rows ?? []).map((r) => r.name));

  for (const file of files) {
    if (applied.has(file)) continue;
    const raw = fs.readFileSync(path.join(dir, file), 'utf8');
    // drizzle-kit عبارات را با این جداکننده تفکیک می‌کند
    const statements = raw.split('--> statement-breakpoint').map((s) => s.trim()).filter(Boolean);
    for (const statement of statements) {
      await db.execute(sql.raw(statement));
    }
    await db.execute(sql`INSERT INTO __migrations (name) VALUES (${file})`);
  }

  // ایندکس‌های متنی برای جست‌وجوی fallback روی متن نرمال‌شده فارسی
  await db.execute(sql`CREATE EXTENSION IF NOT EXISTS pg_trgm`).catch(() => undefined);
  await db
    .execute(sql`CREATE INDEX IF NOT EXISTS search_documents_trgm_idx ON search_documents USING gin (normalized_text gin_trgm_ops)`)
    .catch(() => undefined);
}

export async function closeDatabase() {
  await client?.close();
  client = null;
  dbInstance = null;
}

export { schema };
