/**
 * Test Script for Production Order Dimension-Based Availability
 * Tests availability checking and confirmation for dimension-based materials
 * 
 * Usage: node test-production-dimension-availability.js [production_order_id]
 */

require('dotenv').config({ path: require('path').join(__dirname, '.env') });
const { sequelize } = require('./models');
const productionService = require('./modules/production/services/productionOrders');
const bomDimensionService = require('./services/bomDimensionService');
const materialAllocationService = require('./services/materialAllocationService');
const { ProductionOrder, BOM, BOMItem, Product, RMInventoryPiece, Inventory } = require('./models');
const logger = require('./utils/logger');

// Colors for console output
const colors = {
  reset: '\x1b[0m',
  bright: '\x1b[1m',
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m',
};

function log(message, color = 'reset') {
  console.log(`${colors[color]}${message}${colors.reset}`);
}

function logSection(title) {
  console.log('\n' + '='.repeat(80));
  log(title, 'bright');
  console.log('='.repeat(80));
}

function logSubsection(title) {
  console.log('\n' + '-'.repeat(80));
  log(title, 'cyan');
  console.log('-'.repeat(80));
}

/**
 * Test 1: Check if production order exists and get its details
 */
async function testGetProductionOrder(poId) {
  logSection('TEST 1: Get Production Order Details');
  
  try {
    const po = await ProductionOrder.findByPk(poId, {
      include: [
        {
          model: Product,
          as: 'finishedGoodProduct',
          attributes: ['id', 'name', 'sku', 'product_type', 'track_by_dimensions']
        }
      ]
    });

    if (!po) {
      log(`❌ Production Order ${poId} not found`, 'red');
      return null;
    }

    log(`✅ Production Order ${poId} found`, 'green');
    console.log(JSON.stringify({
      id: po.id,
      fg_product_id: po.fg_product_id,
      quantity: po.quantity,
      status: po.status,
      fgProduct: {
        id: po.finishedGoodProduct?.id,
        name: po.finishedGoodProduct?.name,
        sku: po.finishedGoodProduct?.sku,
        track_by_dimensions: po.finishedGoodProduct?.track_by_dimensions
      }
    }, null, 2));

    return po;
  } catch (error) {
    log(`❌ Error getting production order: ${error.message}`, 'red');
    console.error(error.stack);
    return null;
  }
}

/**
 * Test 2: Get BOM for the production order
 */
async function testGetBOM(fgProductId) {
  logSection('TEST 2: Get BOM for Finished Good Product');
  
  try {
    const bom = await BOM.findOne({
      where: { fg_product_id: fgProductId },
      include: [
        {
          model: Product,
          as: 'finishedGoodProduct',
          attributes: ['id', 'name', 'sku']
        },
        {
          model: BOMItem,
          as: 'items',
          include: [
            {
              model: Product,
              as: 'rawMaterial',
              attributes: ['id', 'name', 'sku', 'product_type', 'track_by_dimensions', 'unit_of_measure']
            }
          ]
        }
      ]
    });

    if (!bom) {
      log(`❌ No BOM found for FG Product ${fgProductId}`, 'red');
      return null;
    }

    log(`✅ BOM ${bom.id} found`, 'green');
    console.log(JSON.stringify({
      id: bom.id,
      fg_product_id: bom.fg_product_id,
      fgProduct: {
        id: bom.finishedGoodProduct?.id,
        name: bom.finishedGoodProduct?.name
      },
      items: bom.items.map(item => ({
        id: item.id,
        rm_product_id: item.rm_product_id,
        item_type: item.item_type,
        required_length: item.required_length,
        required_width: item.required_width,
        dimension_unit: item.dimension_unit,
        quantity_per_unit: item.quantity_per_unit,
        rawMaterial: {
          id: item.rawMaterial?.id,
          name: item.rawMaterial?.name,
          track_by_dimensions: item.rawMaterial?.track_by_dimensions,
          unit_of_measure: item.rawMaterial?.unit_of_measure
        },
        isDimensionBased: !!(item.required_length && item.required_width && item.dimension_unit)
      }))
    }, null, 2));

    return bom;
  } catch (error) {
    log(`❌ Error getting BOM: ${error.message}`, 'red');
    console.error(error.stack);
    return null;
  }
}

/**
 * Test 3: Check inventory pieces for dimension-based items
 */
async function testGetInventoryPieces(bom) {
  logSection('TEST 3: Check Inventory Pieces for Dimension-Based Items');
  
  const results = {};
  
  for (const item of bom.items) {
    const isDimensionBased = !!(item.required_length && item.required_width && item.dimension_unit);
    
    if (isDimensionBased) {
      logSubsection(`Item: ${item.rawMaterial?.name} (ID: ${item.rm_product_id})`);
      
      try {
        const { Op } = require('sequelize');
        const pieces = await RMInventoryPiece.findAll({
          where: {
            product_id: item.rm_product_id,
            status: { [Op.in]: ['FULL', 'USABLE', 'WASTE'] }
          },
          order: [['status', 'DESC'], ['created_at', 'ASC']]
        });

        log(`Found ${pieces.length} inventory pieces`, pieces.length > 0 ? 'green' : 'yellow');
        
        let totalArea = 0;
        const piecesDetail = [];

        for (const piece of pieces) {
          const dims = materialAllocationService.getAvailableDimensions(piece);
          if (dims) {
            const area = parseFloat(dims.length) * parseFloat(dims.width);
            totalArea += area;
            
            piecesDetail.push({
              id: piece.id,
              status: piece.status,
              length: piece.length,
              width: piece.width,
              unit: piece.unit,
              usable_length: piece.usable_length,
              usable_width: piece.usable_width,
              availableDimensions: dims,
              area: parseFloat(area.toFixed(3))
            });
          }
        }

        results[item.rm_product_id] = {
          productName: item.rawMaterial?.name,
          piecesCount: pieces.length,
          pieces: piecesDetail,
          totalArea: parseFloat(totalArea.toFixed(3)),
          requiredLength: item.required_length,
          requiredWidth: item.required_width,
          requiredUnit: item.dimension_unit
        };

        console.log(JSON.stringify({
          piecesCount: pieces.length,
          totalArea: totalArea.toFixed(3) + ` ${item.dimension_unit}²`,
          pieces: piecesDetail
        }, null, 2));

      } catch (error) {
        log(`❌ Error getting inventory pieces: ${error.message}`, 'red');
        console.error(error.stack);
        results[item.rm_product_id] = { error: error.message };
      }
    } else {
      logSubsection(`Item: ${item.rawMaterial?.name} (ID: ${item.rm_product_id}) - Quantity-Based`);
      
      try {
        const inventory = await Inventory.findOne({
          where: { product_id: item.rm_product_id },
          include: [
            {
              model: Product,
              as: 'product',
              attributes: ['id', 'name', 'sku']
            }
          ]
        });

        if (inventory) {
          log(`✅ Inventory found: ${parseFloat(inventory.quantity)} units`, 'green');
          results[item.rm_product_id] = {
            productName: item.rawMaterial?.name,
            quantity: parseFloat(inventory.quantity),
            quantityRequired: item.quantity_per_unit,
            available: parseFloat(inventory.quantity) >= (item.quantity_per_unit || 0)
          };
        } else {
          log(`⚠️  No inventory record found`, 'yellow');
          results[item.rm_product_id] = {
            productName: item.rawMaterial?.name,
            quantity: 0,
            available: false
          };
        }
      } catch (error) {
        log(`❌ Error getting inventory: ${error.message}`, 'red');
        results[item.rm_product_id] = { error: error.message };
      }
    }
  }

  return results;
}

/**
 * Test 4: Test BOM feasibility check
 */
async function testBOMFeasibility(bom, productionQuantity) {
  logSection('TEST 4: Test BOM Feasibility Check');
  
  try {
    log(`Testing feasibility for ${productionQuantity} units...`, 'blue');
    
    const feasibilityResult = await bomDimensionService.validateBOMFeasibility(bom, productionQuantity);
    
    log(`✅ Feasibility check completed`, 'green');
    console.log(JSON.stringify(feasibilityResult, null, 2));
    
    if (feasibilityResult.overallFeasible) {
      log(`✅ All materials are available!`, 'green');
    } else {
      log(`❌ Some materials are not available`, 'red');
    }
    
    return feasibilityResult;
  } catch (error) {
    log(`❌ Error in feasibility check: ${error.message}`, 'red');
    console.error(error.stack);
    return null;
  }
}

/**
 * Test 5: Test availability check via service
 */
async function testAvailabilityCheck(poId) {
  logSection('TEST 5: Test Availability Check via Service');
  
  try {
    log(`Checking availability for Production Order ${poId}...`, 'blue');
    
    const availability = await productionService.checkAvailability(poId);
    
    log(`✅ Availability check completed`, 'green');
    console.log(JSON.stringify(availability, null, 2));
    
    if (availability.all_available) {
      log(`✅ All materials are available!`, 'green');
      log(`Max producible units: ${availability.max_producible_units}`, 'green');
    } else {
      log(`❌ Some materials are not available`, 'red');
      log(`Max producible units: ${availability.max_producible_units || 0}`, 'yellow');
      
      // Show details for unavailable items
      availability.items.forEach(item => {
        if (!item.available) {
          log(`\n❌ ${item.rm_product_name || 'Unknown'}:`, 'red');
          if (item.error) {
            log(`   Error: ${item.error}`, 'red');
          }
          if (item.shortfall !== undefined) {
            log(`   Shortfall: ${item.shortfall}`, 'red');
          }
          if (item.available_area !== undefined && item.total_area_required !== undefined) {
            log(`   Available: ${item.available_area} ${item.dimensions?.unit || ''}²`, 'yellow');
            log(`   Required: ${item.total_area_required} ${item.dimensions?.unit || ''}²`, 'red');
          }
        }
      });
    }
    
    return availability;
  } catch (error) {
    log(`❌ Error in availability check: ${error.message}`, 'red');
    console.error(error.stack);
    return null;
  }
}

/**
 * Test 6: Test material allocation for a specific BOM item
 */
async function testMaterialAllocation(bom, productionQuantity) {
  logSection('TEST 6: Test Material Allocation for Each BOM Item');
  
  for (const bomItem of bom.items) {
    const isDimensionBased = !!(bomItem.required_length && bomItem.required_width && bomItem.dimension_unit);
    
    if (isDimensionBased) {
      logSubsection(`Allocating: ${bomItem.rawMaterial?.name}`);
      
      try {
        const { Op } = require('sequelize');
        const availablePieces = await RMInventoryPiece.findAll({
          where: {
            product_id: bomItem.rm_product_id,
            status: { [Op.in]: ['FULL', 'USABLE', 'WASTE'] }
          },
          order: [['status', 'DESC'], ['created_at', 'ASC']]
        });

        log(`Found ${availablePieces.length} pieces`, availablePieces.length > 0 ? 'green' : 'yellow');

        const bomItemForAllocation = {
          rm_product_id: bomItem.rm_product_id,
          required_length: bomItem.required_length,
          required_width: bomItem.required_width,
          dimension_unit: bomItem.dimension_unit,
          isDimensionBased: () => true
        };

        const allocationResult = materialAllocationService.allocateMaterialForBOMItem(
          availablePieces,
          bomItemForAllocation,
          productionQuantity,
          { strategy: materialAllocationService.ALLOCATION_STRATEGIES.BEST_FIT }
        );

        log(`✅ Allocation check completed`, allocationResult.isValid ? 'green' : 'red');
        console.log(JSON.stringify({
          isValid: allocationResult.isValid,
          isFullyAllocated: allocationResult.isFullyAllocated,
          totalAllocatedUnits: allocationResult.totalAllocatedUnits,
          remainingQuantity: allocationResult.remainingQuantity,
          piecesUsed: allocationResult.piecesUsed?.length || 0,
          totalWasteArea: allocationResult.totalWasteArea,
          error: allocationResult.error
        }, null, 2));

        if (!allocationResult.isValid) {
          log(`❌ Allocation failed: ${allocationResult.error}`, 'red');
        } else if (!allocationResult.isFullyAllocated) {
          log(`⚠️  Partially allocated: ${allocationResult.totalAllocatedUnits}/${productionQuantity} units`, 'yellow');
        } else {
          log(`✅ Fully allocated: ${allocationResult.totalAllocatedUnits} units`, 'green');
        }

      } catch (error) {
        log(`❌ Error in allocation: ${error.message}`, 'red');
        console.error(error.stack);
      }
    }
  }
}

/**
 * Main test function
 */
async function runTests() {
  const poId = process.argv[2] ? parseInt(process.argv[2]) : 2;
  
  log('\n' + '='.repeat(80), 'bright');
  log('PRODUCTION ORDER DIMENSION-BASED AVAILABILITY TEST', 'bright');
  log('='.repeat(80), 'bright');
  log(`Testing Production Order ID: ${poId}`, 'blue');

  try {
    // Connect to database
    await sequelize.authenticate();
    log('\n✅ Database connection established', 'green');

    // Test 1: Get production order
    const po = await testGetProductionOrder(poId);
    if (!po) {
      log('\n❌ Cannot continue without production order', 'red');
      process.exit(1);
    }

    // Test 2: Get BOM
    const bom = await testGetBOM(po.fg_product_id);
    if (!bom) {
      log('\n❌ Cannot continue without BOM', 'red');
      process.exit(1);
    }

    // Test 3: Check inventory pieces
    await testGetInventoryPieces(bom);

    // Test 4: Test BOM feasibility
    await testBOMFeasibility(bom, po.quantity);

    // Test 5: Test availability check
    await testAvailabilityCheck(poId);

    // Test 6: Test material allocation
    await testMaterialAllocation(bom, po.quantity);

    logSection('TEST SUMMARY');
    log('All tests completed!', 'green');
    log('\nReview the output above to identify any issues.', 'yellow');

  } catch (error) {
    log(`\n❌ Fatal error: ${error.message}`, 'red');
    console.error(error.stack);
    process.exit(1);
  } finally {
    await sequelize.close();
    log('\n✅ Database connection closed', 'green');
  }
}

// Run tests
runTests().catch(error => {
  console.error('Unhandled error:', error);
  process.exit(1);
});
