/**
 * Sync BlackBox Stocks Options Flow Data
 * Fetches latest flow data from BlackBox API and updates the database
 * 
 * Usage: 
 *   node scripts/syncBlackBoxFlow.js
 *   node scripts/syncBlackBoxFlow.js --start-date 2024-01-15 --end-date 2024-01-16
 *   node scripts/syncBlackBoxFlow.js --limit 100
 */

import { fetchBlackBoxFlow, mapBlackBoxToDatabase } from '../src/services/blackboxApiService.js';
import { rawQuery } from '../src/db.js';
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

// Get the directory of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Load .env from backend root (parent directory of scripts)
dotenv.config({ path: join(__dirname, '..', '.env') });

/**
 * Ensure OptionsFlow_monthly table exists
 */
async function ensureTableExists() {
  const createTableSQL = `
    CREATE TABLE IF NOT EXISTS "OptionsFlow_monthly" (
      "CreatedDate" TEXT,
      "CreatedTime" TEXT,
      "Symbol" TEXT,
      "Type" TEXT,
      "Volume" TEXT,
      "Price" TEXT,
      "Side" TEXT,
      "CallPut" TEXT,
      "Strike" TEXT,
      "Spot" TEXT,
      "Premium" TEXT,
      "ExpirationDate" TEXT,
      "Color" TEXT,
      "ImpliedVolatility" TEXT,
      "Dte" TEXT,
      "ER" TEXT,
      "StockEtf" TEXT,
      "Sector" TEXT,
      "Uoa" TEXT,
      "Weekly" TEXT,
      "MktCap" TEXT,
      "OI" TEXT
    );
  `;
  
  await rawQuery(createTableSQL);
  console.log('✅ Table OptionsFlow_monthly verified');
}

/**
 * Insert flow records into database
 * @param {Array} records - Array of mapped records
 * @param {boolean} skipDuplicates - Skip duplicate records based on key fields
 */
async function insertFlowRecords(records, skipDuplicates = true) {
  if (records.length === 0) {
    console.log('   No records to insert');
    return 0;
  }

  const batchSize = 100;
  let inserted = 0;
  let skipped = 0;
  let errors = [];

  for (let i = 0; i < records.length; i += batchSize) {
    const batch = records.slice(i, i + batchSize);
    
    // Build parameterized query
    const placeholders = batch.map((_, idx) => {
      const start = idx * 22 + 1;
      return `($${start}, $${start + 1}, $${start + 2}, $${start + 3}, $${start + 4}, $${start + 5}, $${start + 6}, $${start + 7}, $${start + 8}, $${start + 9}, $${start + 10}, $${start + 11}, $${start + 12}, $${start + 13}, $${start + 14}, $${start + 15}, $${start + 16}, $${start + 17}, $${start + 18}, $${start + 19}, $${start + 20}, $${start + 21})`;
    }).join(', ');
    
    const params = batch.flatMap(row => [
      row.CreatedDate || null,
      row.CreatedTime || null,
      row.Symbol || null,
      row.Type || null,
      row.Volume || null,
      row.Price || null,
      row.Side || null,
      row.CallPut || null,
      row.Strike || null,
      row.Spot || null,
      row.Premium || null,
      row.ExpirationDate || null,
      row.Color || null,
      row.ImpliedVolatility || null,
      row.Dte || null,
      row.ER || null,
      row.StockEtf || null,
      row.Sector || null,
      row.Uoa || null,
      row.Weekly || null,
      row.MktCap || null,
      row.OI || null
    ]);
    
    const insertSQL = skipDuplicates ? `
      INSERT INTO "OptionsFlow_monthly" (
        "CreatedDate", "CreatedTime", "Symbol", "Type", "Volume", "Price",
        "Side", "CallPut", "Strike", "Spot", "Premium", "ExpirationDate",
        "Color", "ImpliedVolatility", "Dte", "ER", "StockEtf", "Sector",
        "Uoa", "Weekly", "MktCap", "OI"
      ) VALUES ${placeholders}
      ON CONFLICT DO NOTHING
    ` : `
      INSERT INTO "OptionsFlow_monthly" (
        "CreatedDate", "CreatedTime", "Symbol", "Type", "Volume", "Price",
        "Side", "CallPut", "Strike", "Spot", "Premium", "ExpirationDate",
        "Color", "ImpliedVolatility", "Dte", "ER", "StockEtf", "Sector",
        "Uoa", "Weekly", "MktCap", "OI"
      ) VALUES ${placeholders}
    `;
    
    try {
      // Try with ON CONFLICT first (if unique constraint exists)
      await rawQuery(insertSQL, params);
      
      // Note: rawQuery returns rows array, not rowCount
      // We verify actual inserts by checking count before/after
      inserted += batch.length;
      console.log(`   ✅ Inserted batch ${Math.floor(i / batchSize) + 1}: ${batch.length} records (Total: ${inserted})`);
    } catch (error) {
      // If ON CONFLICT fails (no unique constraint), use simple INSERT
      if (error.message.includes('ON CONFLICT') || 
          error.message.includes('syntax error') || 
          error.message.includes('there is no unique or exclusion constraint')) {
        console.log(`   ⚠️  ON CONFLICT not supported (no unique constraint), using simple INSERT...`);
        const simpleInsertSQL = `
          INSERT INTO "OptionsFlow_monthly" (
            "CreatedDate", "CreatedTime", "Symbol", "Type", "Volume", "Price",
            "Side", "CallPut", "Strike", "Spot", "Premium", "ExpirationDate",
            "Color", "ImpliedVolatility", "Dte", "ER", "StockEtf", "Sector",
            "Uoa", "Weekly", "MktCap", "OI"
          ) VALUES ${placeholders}
        `;
        try {
          await rawQuery(simpleInsertSQL, params);
          inserted += batch.length;
          console.log(`   ✅ Inserted batch ${Math.floor(i / batchSize) + 1}: ${batch.length} records (Total: ${inserted})`);
        } catch (insertError) {
          console.error(`   ❌ Error inserting batch ${Math.floor(i / batchSize) + 1}:`, insertError.message);
          errors.push({ batch: Math.floor(i / batchSize) + 1, error: insertError.message });
          // Continue with next batch instead of throwing
          skipped += batch.length;
        }
      } else {
        console.error(`   ❌ Error inserting batch ${Math.floor(i / batchSize) + 1}:`, error.message);
        errors.push({ batch: Math.floor(i / batchSize) + 1, error: error.message });
        // Continue with next batch instead of throwing
        skipped += batch.length;
      }
    }
  }

  if (skipped > 0) {
    console.log(`\n⚠️  Skipped ${skipped} records due to errors`);
    if (errors.length > 0) {
      console.log('   Error summary:');
      errors.slice(0, 5).forEach(e => console.log(`   - Batch ${e.batch}: ${e.error}`));
      if (errors.length > 5) {
        console.log(`   ... and ${errors.length - 5} more errors`);
      }
    }
  }

  return inserted;
}

/**
 * Main sync function
 */
async function syncBlackBoxFlow() {
  console.log('🚀 Starting BlackBox Stocks flow data sync...\n');

  try {
    // Parse command line arguments
    const args = process.argv.slice(2);
    const options = {};
    
    for (let i = 0; i < args.length; i++) {
      if (args[i] === '--start-date' && args[i + 1]) {
        options.startDate = args[i + 1];
        i++;
      } else if (args[i] === '--end-date' && args[i + 1]) {
        options.endDate = args[i + 1];
        i++;
      } else if (args[i] === '--limit' && args[i + 1]) {
        options.count = parseInt(args[i + 1]);
        i++;
      } else if (args[i] === '--count' && args[i + 1]) {
        options.count = parseInt(args[i + 1]);
        i++;
      } else if (args[i] === '--symbol' && args[i + 1]) {
        options.symbol = args[i + 1];
        i++;
      }
    }

    // Default to today if no dates provided
    if (!options.startDate && !options.endDate) {
      const today = new Date().toISOString().split('T')[0];
      options.startDate = today;
      options.endDate = today;
      console.log(`📅 No date range specified, using today: ${today}`);
    }

    console.log(`📥 Fetching flow data from BlackBox API...`);
    console.log(`   Options:`, options);

    // Ensure table exists
    await ensureTableExists();

    // Fetch data from API
    const apiRecords = await fetchBlackBoxFlow(options);
    console.log(`   ✅ Fetched ${apiRecords.length} records from API`);

    if (apiRecords.length === 0) {
      console.log('⚠️  No records returned from API');
      return;
    }

    // Log sample record to understand structure
    if (apiRecords.length > 0) {
      console.log('\n📋 Sample API record structure:');
      console.log(JSON.stringify(apiRecords[0], null, 2));
    }

    // Map API records to database schema
    console.log('\n🔄 Mapping records to database schema...');
    const mappedRecords = apiRecords.map(mapBlackBoxToDatabase);
    console.log(`   ✅ Mapped ${mappedRecords.length} records`);

    // Log sample mapped record
    if (mappedRecords.length > 0) {
      console.log('\n📋 Sample mapped record:');
      console.log(JSON.stringify(mappedRecords[0], null, 2));
    }

    // Get count before insert
    const countBefore = await rawQuery('SELECT COUNT(*) as count FROM "OptionsFlow_monthly"');
    const beforeCount = parseInt(countBefore[0]?.count || 0);
    console.log(`\n📊 Records in DB before insert: ${beforeCount}`);

    // Insert into database
    console.log('\n💾 Inserting records into database...');
    const inserted = await insertFlowRecords(mappedRecords, true);
    
    // Get count after insert to verify
    const countAfter = await rawQuery('SELECT COUNT(*) as count FROM "OptionsFlow_monthly"');
    const afterCount = parseInt(countAfter[0]?.count || 0);
    const actuallyInserted = afterCount - beforeCount;

    console.log(`\n✅ Insert operation completed`);
    console.log(`   Attempted to insert: ${inserted} records`);
    console.log(`   Actually inserted: ${actuallyInserted} records`);

    // Summary
    console.log('\n📊 Sync Summary:');
    console.log(`   Fetched from API: ${apiRecords.length} records`);
    console.log(`   Attempted inserts: ${inserted} records`);
    console.log(`   Actually inserted: ${actuallyInserted} records`);
    console.log(`   Records before: ${beforeCount}`);
    console.log(`   Records after: ${afterCount}`);
    console.log(`   Total records in DB: ${afterCount}`);
    
    if (actuallyInserted < inserted) {
      const diff = inserted - actuallyInserted;
      console.log(`\n⚠️  Warning: ${diff} records may have been duplicates or failed to insert`);
    }

  } catch (error) {
    console.error('\n❌ Sync failed:', error.message);
    console.error(error);
    process.exit(1);
  }
}

// Run if called directly
const isMainModule = import.meta.url === `file://${process.argv[1]}` || 
                     import.meta.url.endsWith('syncBlackBoxFlow.js') ||
                     process.argv[1]?.endsWith('syncBlackBoxFlow.js');

if (isMainModule || !process.env.NODE_ENV) {
  syncBlackBoxFlow().catch(error => {
    console.error('Fatal error:', error);
    process.exit(1);
  });
}

export { syncBlackBoxFlow };

