#!/usr/bin/env node
/**
 * SQLite → D1 data migration script for Payment Kit
 *
 * Reads from the local blocklet-server SQLite database and generates
 * SQL files that can be imported into D1 via `wrangler d1 execute`.
 *
 * Uses sqlite3 CLI (no npm dependencies needed).
 *
 * Usage:
 *   node cloudflare/migrate-to-d1.js [--dry-run] [--table <name>] [--skip-events]
 */

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

// Parse args
const args = process.argv.slice(2);
const flags = {
  dryRun: args.includes('--dry-run'),
  skipEvents: args.includes('--skip-events'),
  batchSize: 50,
  tables: [],
  dbPath: path.join(
    process.env.HOME,
    'blocklet-server-data/.blocklet-server/data',
    'zNKuN3XwXN7xq2NsJQjjfwujyqCxx26DhwgV',
    'z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk',
    'payment-kit.db'
  ),
  outputDir: path.resolve(__dirname, 'migration-sql'),
};

for (let i = 0; i < args.length; i++) {
  if (args[i] === '--batch-size' && args[i + 1]) flags.batchSize = parseInt(args[++i], 10);
  if (args[i] === '--table' && args[i + 1]) flags.tables.push(args[++i]);
  if (args[i] === '--db-path' && args[i + 1]) flags.dbPath = args[++i];
  if (args[i] === '--output-dir' && args[i + 1]) flags.outputDir = args[++i];
}

// Tables to skip
const SKIP_TABLES = ['customers_backup', 'payment_currencies_tmp', 'SequelizeMeta'];

function sqlite3(dbPath, query) {
  try {
    return execSync(`sqlite3 "${dbPath}" "${query}"`, {
      encoding: 'utf-8',
      maxBuffer: 100 * 1024 * 1024, // 100MB buffer
    }).trim();
  } catch (err) {
    console.error(`sqlite3 error: ${err.message}`);
    return '';
  }
}

function sqlite3Json(dbPath, query) {
  try {
    const result = execSync(`sqlite3 -json "${dbPath}" "${query}"`, {
      encoding: 'utf-8',
      maxBuffer: 100 * 1024 * 1024,
    }).trim();
    return result ? JSON.parse(result) : [];
  } catch (err) {
    console.error(`sqlite3 json error for query: ${query.slice(0, 100)}`);
    console.error(err.message.slice(0, 200));
    return [];
  }
}

function escapeSQL(value) {
  if (value === null || value === undefined) return 'NULL';
  if (typeof value === 'number') return String(value);
  if (typeof value === 'boolean') return value ? '1' : '0';
  const str = String(value).replace(/'/g, "''");
  return `'${str}'`;
}

function main() {
  console.log('=== Payment Kit SQLite → D1 Migration ===');
  console.log(`Source: ${flags.dbPath}`);
  console.log(`Output: ${flags.outputDir}`);
  console.log(`Dry run: ${flags.dryRun}`);
  console.log();

  if (!fs.existsSync(flags.dbPath)) {
    console.error(`ERROR: Source database not found: ${flags.dbPath}`);
    process.exit(1);
  }

  // Get all tables
  const tablesRaw = sqlite3(flags.dbPath, '.tables');
  const allTables = tablesRaw.split(/\s+/).filter(Boolean).sort();

  const tables = flags.tables.length > 0
    ? flags.tables.filter(t => allTables.includes(t))
    : allTables.filter(t => !SKIP_TABLES.includes(t));

  if (flags.skipEvents) {
    const idx = tables.indexOf('events');
    if (idx > -1) tables.splice(idx, 1);
  }

  console.log(`Tables to migrate: ${tables.length}`);
  console.log();

  // Create output directory
  fs.mkdirSync(flags.outputDir, { recursive: true });

  // Phase 1: Generate schema SQL
  const schemaFile = path.join(flags.outputDir, '000-schema.sql');
  const schemaLines = [];

  for (const table of tables) {
    const createSQL = sqlite3(flags.dbPath,
      `SELECT sql FROM sqlite_master WHERE type='table' AND name='${table}';`
    );
    if (createSQL) {
      const sql = createSQL.replace(
        /CREATE TABLE\s+/i,
        'CREATE TABLE IF NOT EXISTS '
      );
      schemaLines.push(sql + ';');
      schemaLines.push('');
    }
  }

  // Indexes
  const indexSQL = sqlite3(flags.dbPath,
    "SELECT sql FROM sqlite_master WHERE type='index' AND sql IS NOT NULL ORDER BY name;"
  );
  if (indexSQL) {
    schemaLines.push('-- Indexes');
    for (const line of indexSQL.split('\n').filter(Boolean)) {
      const sql = line
        .replace(/CREATE INDEX\s+/i, 'CREATE INDEX IF NOT EXISTS ')
        .replace(/CREATE UNIQUE INDEX\s+/i, 'CREATE UNIQUE INDEX IF NOT EXISTS ');
      schemaLines.push(sql + ';');
    }
  }

  fs.writeFileSync(schemaFile, schemaLines.join('\n'));
  console.log(`Schema written: ${schemaFile}`);

  // Phase 2: Generate data SQL files per table
  let totalRows = 0;
  let fileIndex = 1;

  for (const table of tables) {
    const countStr = sqlite3(flags.dbPath, `SELECT COUNT(*) FROM "${table}";`);
    const count = parseInt(countStr, 10) || 0;

    if (count === 0) {
      console.log(`  ${table}: 0 rows (skip)`);
      continue;
    }

    console.log(`  ${table}: ${count} rows — exporting...`);

    // Get column names
    const colInfoRaw = sqlite3(flags.dbPath, `PRAGMA table_info("${table}");`);
    const colNames = colInfoRaw.split('\n').filter(Boolean).map(line => {
      const parts = line.split('|');
      return parts[1]; // column name is the second field
    });

    // Export in pages to avoid memory issues
    const pageSize = 500;
    let offset = 0;
    let partIndex = 0;
    const paddedIdx = String(fileIndex).padStart(3, '0');

    while (offset < count) {
      const rows = sqlite3Json(flags.dbPath,
        `SELECT * FROM "${table}" LIMIT ${pageSize} OFFSET ${offset};`
      );
      if (!rows || rows.length === 0) break;

      const sqlLines = [];
      sqlLines.push(`-- Table: ${table} (rows ${offset + 1}-${offset + rows.length} of ${count})`);

      // Generate INSERT statements in batches
      for (let i = 0; i < rows.length; i += flags.batchSize) {
        const batch = rows.slice(i, i + flags.batchSize);
        const values = batch.map(row => {
          const vals = colNames.map(col => escapeSQL(row[col]));
          return `(${vals.join(', ')})`;
        });

        sqlLines.push(
          `INSERT OR IGNORE INTO "${table}" (${colNames.map(c => `"${c}"`).join(', ')}) VALUES`
        );
        sqlLines.push(values.join(',\n') + ';');
        sqlLines.push('');
      }

      const partSuffix = partIndex > 0 ? `-part${partIndex}` : '';
      const dataFile = path.join(flags.outputDir, `${paddedIdx}-${table}${partSuffix}.sql`);
      fs.writeFileSync(dataFile, sqlLines.join('\n'));

      partIndex++;
      offset += rows.length;
    }

    totalRows += count;
    fileIndex++;
  }

  console.log();
  console.log(`Total: ${totalRows} rows across ${tables.length} tables`);
  console.log(`SQL files generated in: ${flags.outputDir}`);

  if (flags.dryRun) {
    console.log('\nDry run — skipping D1 import.');
    console.log('To import, run:');
    console.log(`  for f in ${flags.outputDir}/*.sql; do`);
    console.log('    echo "Importing $f..."');
    console.log(`    npx wrangler d1 execute payment-kit-dev --local --file="$f" --config=cloudflare/wrangler.jsonc`);
    console.log('  done');
    return;
  }

  // Phase 3: Execute SQL files against D1
  console.log('\n=== Importing into D1 ===');
  const sqlFiles = fs.readdirSync(flags.outputDir)
    .filter(f => f.endsWith('.sql'))
    .sort();

  const cwd = path.resolve(__dirname, '..');
  for (const file of sqlFiles) {
    const filePath = path.join(flags.outputDir, file);
    const stat = fs.statSync(filePath);
    const sizeMB = (stat.size / 1024 / 1024).toFixed(2);

    process.stdout.write(`  ${file} (${sizeMB}MB)... `);
    try {
      execSync(
        `npx wrangler d1 execute payment-kit-dev --local --file="${filePath}" --config=cloudflare/wrangler.jsonc`,
        { cwd, stdio: 'pipe', timeout: 120000 }
      );
      console.log('OK');
    } catch (err) {
      const errMsg = err.stderr?.toString()?.slice(0, 300) || err.message;
      console.log('FAILED');
      console.error(`    ${errMsg}`);
    }
  }

  console.log('\nMigration complete!');
}

main();
