const { outputProgress, formatElapsedTime } = require('../metrics-new/utils/progress');

async function importCategories(prodConnection, localConnection) {
  outputProgress({
    operation: "Starting categories import",
    status: "running",
  });

  const startTime = Date.now();
  const typeOrder = [10, 20, 11, 21, 12, 13];
  let totalInserted = 0;
  let totalUpdated = 0;
  let skippedCategories = [];

  try {
    // Start a single transaction for the entire import.
    // Must use the wrapper's beginTransaction() (dedicated client) — query('BEGIN')
    // checks out a client per call, so BEGIN/work/COMMIT would not be guaranteed
    // to share a connection.
    await localConnection.beginTransaction();

    // Temporarily disable the trigger that's causing problems.
    // ALTER TABLE ... DISABLE TRIGGER is transactional: a rollback restores it.
    await localConnection.query('ALTER TABLE categories DISABLE TRIGGER update_categories_updated_at');

    // Process each type in order with its own savepoint
    for (const type of typeOrder) {
      try {
        // Create a savepoint for this type
        await localConnection.query(`SAVEPOINT category_type_${type}`);

        // Production query remains MySQL compatible
        const [categories] = await prodConnection.query(
          `
          SELECT 
            pc.cat_id,
            pc.name,
            pc.type,
            CASE 
              WHEN pc.type IN (10, 20) THEN NULL  -- Top level categories should have no parent
              WHEN pc.master_cat_id IS NULL THEN NULL
              ELSE pc.master_cat_id
            END as parent_id,
            pc.combined_name as description
          FROM product_categories pc
          WHERE pc.type = ?
          ORDER BY pc.cat_id
        `,
          [type]
        );

        if (categories.length === 0) {
          await localConnection.query(`RELEASE SAVEPOINT category_type_${type}`);
          continue;
        }

        console.log(`Processing ${categories.length} type ${type} categories`);

        // For types that can have parents (11, 21, 12, 13), we'll proceed directly
        // No need to check for parent existence since we process in hierarchical order
        let categoriesToInsert = categories;

        if (categoriesToInsert.length === 0) {
          console.log(`No valid categories of type ${type} to insert`);
          await localConnection.query(`RELEASE SAVEPOINT category_type_${type}`);
          continue;
        }

        // PostgreSQL upsert query with parameterized values
        const values = categoriesToInsert.flatMap((cat) => [
          cat.cat_id,
          cat.name,
          cat.type,
          cat.parent_id,
          cat.description,
          'active',
          new Date(),
          new Date()
        ]);

        const placeholders = categoriesToInsert
          .map((_, i) => `($${i * 8 + 1}, $${i * 8 + 2}, $${i * 8 + 3}, $${i * 8 + 4}, $${i * 8 + 5}, $${i * 8 + 6}, $${i * 8 + 7}, $${i * 8 + 8})`)
          .join(',');

        // Insert categories with ON CONFLICT clause for PostgreSQL
        const query = `
          WITH inserted_categories AS (
            INSERT INTO categories (
              cat_id, name, type, parent_id, description, status, created_at, updated_at
            )
            VALUES ${placeholders}
            ON CONFLICT (cat_id) DO UPDATE SET
              name = EXCLUDED.name,
              type = EXCLUDED.type,
              parent_id = EXCLUDED.parent_id,
              description = EXCLUDED.description,
              status = EXCLUDED.status,
              updated_at = EXCLUDED.updated_at
            WHERE -- Only update if at least one field has changed
              categories.name IS DISTINCT FROM EXCLUDED.name OR
              categories.type IS DISTINCT FROM EXCLUDED.type OR
              categories.parent_id IS DISTINCT FROM EXCLUDED.parent_id OR
              categories.description IS DISTINCT FROM EXCLUDED.description OR
              categories.status IS DISTINCT FROM EXCLUDED.status
            RETURNING 
              cat_id,
              CASE 
                WHEN xmax = 0 THEN true
                ELSE false
              END as is_insert
          )
          SELECT 
            COUNT(*) as total,
            COUNT(*) FILTER (WHERE is_insert) as inserted,
            COUNT(*) FILTER (WHERE NOT is_insert) as updated
          FROM inserted_categories`;
        
        const result = await localConnection.query(query, values);

        // Get the first result since query returns an array
        const queryResult = Array.isArray(result) ? result[0] : result;

        if (!queryResult || !queryResult.rows || !queryResult.rows[0]) {
          console.error('Query failed to return results');
          throw new Error('Query did not return expected results');
        }

        const total = parseInt(queryResult.rows[0].total) || 0;
        const inserted = parseInt(queryResult.rows[0].inserted) || 0;
        const updated = parseInt(queryResult.rows[0].updated) || 0;

        console.log(`Total: ${total}, Inserted: ${inserted}, Updated: ${updated}`);
        
        totalInserted += inserted;
        totalUpdated += updated;

        // Release the savepoint for this type
        await localConnection.query(`RELEASE SAVEPOINT category_type_${type}`);

        outputProgress({
          status: "running",
          operation: "Categories import",
          message: `Imported ${inserted} (updated ${updated}) categories of type ${type}`,
          current: totalInserted + totalUpdated,
          total: categories.length,
          elapsed: formatElapsedTime(startTime),
        });
      } catch (error) {
        // Rollback to the savepoint for this type
        await localConnection.query(`ROLLBACK TO SAVEPOINT category_type_${type}`);
        throw error;
      }
    }

    // Re-enable the trigger INSIDE the transaction so disable/enable are atomic
    await localConnection.query('ALTER TABLE categories ENABLE TRIGGER update_categories_updated_at');

    // Commit the entire transaction - we'll do this even if we have skipped categories
    await localConnection.commit();

    // Update sync status
    await localConnection.query(`
      INSERT INTO sync_status (table_name, last_sync_timestamp)
      VALUES ('categories', NOW())
      ON CONFLICT (table_name) DO UPDATE SET
        last_sync_timestamp = NOW()
    `);

    outputProgress({
      status: "complete",
      operation: "Categories import completed",
      current: totalInserted + totalUpdated,
      total: totalInserted + totalUpdated,
      duration: formatElapsedTime(startTime),
      warnings: skippedCategories.length > 0 ? {
        message: "Some categories were skipped due to missing parents",
        skippedCategories
      } : undefined
    });

    return {
      status: "complete",
      recordsAdded: totalInserted,
      recordsUpdated: totalUpdated,
      totalRecords: totalInserted + totalUpdated,
      warnings: skippedCategories.length > 0 ? {
        message: "Some categories were skipped due to missing parents",
        skippedCategories
      } : undefined
    };
  } catch (error) {
    console.error("Error importing categories:", error);
    
    // Only rollback if we haven't committed yet. The rollback also restores the
    // trigger state (DISABLE TRIGGER was inside the transaction).
    try {
      await localConnection.rollback();
    } catch (rollbackError) {
      console.error("Error during rollback:", rollbackError);
    }
    
    outputProgress({
      status: "error",
      operation: "Categories import failed",
      error: error.message
    });

    throw error;
  }
}

module.exports = importCategories; 