const db = require('../models');
const { Op } = require('sequelize');

/**
 * Create Gate Entry from Purchase Order
 * 
 * @param {Integer} purchaseOrderId - Purchase Order ID
 * @param {Object} options - Additional options
 * @param {String} options.gateEntryNumber - Custom gate entry number (optional)
 * @param {Date} options.gateEntryDate - Gate entry date (optional, defaults to today)
 * @param {String} options.invoiceNumber - Vendor invoice number (optional)
 * @param {Date} options.invoiceDate - Vendor invoice date (optional)
 * @param {String} options.lrNumber - Lorry Receipt number (optional)
 * @param {Date} options.lrDate - Lorry Receipt date (optional)
 * @param {String} options.transporterName - Transporter name (optional)
 * @param {String} options.vehicleNumber - Vehicle number (optional)
 * @param {String} options.driverName - Driver name (optional)
 * @param {String} options.driverContact - Driver contact (optional)
 * @param {String} options.destinationType - Destination type: 'STORES' or 'QC' (optional, defaults to 'STORES')
 * @param {Array} options.items - Items to receive (optional, defaults to all items with pending quantities)
 * @param {Integer} options.userId - User ID creating the gate entry
 * @param {Object} options.transaction - Sequelize transaction (optional)
 * @returns {Promise<Object>} Created Gate Entry with items
 */
exports.createGateEntryFromPurchaseOrder = async (purchaseOrderId, options = {}) => {
  const {
    gateEntryNumber = null,
    gateEntryDate = new Date().toISOString().split('T')[0],
    invoiceNumber = null,
    invoiceDate = null,
    lrNumber = null,
    lrDate = null,
    transporterName = null,
    vehicleNumber = null,
    driverName = null,
    driverContact = null,
    destinationType = 'STORES',
    items = null, // If null, receive all items with pending quantities
    userId = null,
    transaction = null
  } = options;

  const txn = transaction || await db.sequelize.transaction();
  const shouldCommit = !transaction;

  try {
    // Load Purchase Order with all necessary data
    const purchaseOrder = await db.PurchaseOrder.findByPk(purchaseOrderId, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.FiscalYear, as: 'fiscalYear' },
        { model: db.BusinessPartner, as: 'vendor' },
        { model: db.Currency, as: 'currency' },
        {
          model: db.PurchaseOrderItem,
          as: 'items',
          include: [
            { model: db.Material, as: 'material' },
            { model: db.UOM, as: 'uom' }
          ]
        }
      ],
      transaction: txn
    });

    if (!purchaseOrder) {
      throw new Error(`Purchase Order with ID ${purchaseOrderId} not found`);
    }

    // Validate Purchase Order has items
    if (!purchaseOrder.items || purchaseOrder.items.length === 0) {
      throw new Error(`Purchase Order ${purchaseOrder.po_number} has no items`);
    }

    // Check if Purchase Order is approved/released
    if (purchaseOrder.status === 'DRAFT') {
      throw new Error(`Purchase Order ${purchaseOrder.po_number} must be approved before creating Gate Entry`);
    }

    // Generate gate entry number if not provided
    let gateEntryNum = gateEntryNumber;
    if (!gateEntryNum) {
      gateEntryNum = await generateGateEntryNumber(purchaseOrder.company_id, purchaseOrder.fiscal_year_id, txn);
    }

    // Check if gate entry number already exists
    const existingGateEntry = await db.GateEntry.findOne({
      where: {
        company_id: purchaseOrder.company_id,
        plant_id: purchaseOrder.plant_id,
        fiscal_year_id: purchaseOrder.fiscal_year_id,
        gate_entry_number: gateEntryNum
      },
      transaction: txn
    });

    if (existingGateEntry) {
      throw new Error(`Gate Entry number ${gateEntryNum} already exists`);
    }

    // Determine which items to receive
    let itemsToReceive = items;
    if (!itemsToReceive) {
      // Receive all items with pending quantities
      itemsToReceive = purchaseOrder.items.map(poItem => {
        const orderedQty = parseFloat(poItem.quantity || 0);
        const receivedQty = parseFloat(poItem.received_quantity || 0);
        const pendingQty = orderedQty - receivedQty;
        
        return {
          po_item_id: poItem.id,
          material_id: poItem.material_id,
          po_quantity: orderedQty,
          received_quantity: pendingQty, // Default to full pending quantity
          unit_price: poItem.unit_price,
          uom_id: poItem.uom_id,
          storage_location: poItem.storage_location || null
        };
      }).filter(item => item.received_quantity > 0);
    }

    if (itemsToReceive.length === 0) {
      throw new Error('No items to receive. All items may already be received.');
    }

    // Validate received quantities don't exceed pending quantities
    for (const item of itemsToReceive) {
      const poItem = purchaseOrder.items.find(pi => pi.id === item.po_item_id);
      if (poItem) {
        const orderedQty = parseFloat(poItem.quantity || 0);
        const receivedQty = parseFloat(poItem.received_quantity || 0);
        const pendingQty = orderedQty - receivedQty;
        const itemReceivedQty = parseFloat(item.received_quantity || 0);

        if (itemReceivedQty > pendingQty) {
          throw new Error(`Cannot receive ${itemReceivedQty} for material ${poItem.material?.material_number || poItem.material_id}. Pending quantity is ${pendingQty}`);
        }
      }
    }

    // Determine initial status based on destination type
    let initialStatus = 'DRAFT';
    if (destinationType === 'STORES') {
      initialStatus = 'GRN_READY'; // Ready for GRN creation
    } else if (destinationType === 'QC') {
      initialStatus = 'QC_PENDING'; // Needs QC approval
    }

    // Create Gate Entry header
    const gateEntryData = {
      company_id: purchaseOrder.company_id,
      plant_id: purchaseOrder.plant_id,
      fiscal_year_id: purchaseOrder.fiscal_year_id,
      gate_entry_number: gateEntryNum,
      gate_entry_date: gateEntryDate,
      po_id: purchaseOrder.id, // Link to Purchase Order
      vendor_id: purchaseOrder.vendor_id,
      invoice_number: invoiceNumber,
      invoice_date: invoiceDate,
      lr_number: lrNumber,
      lr_date: lrDate,
      transporter_name: transporterName,
      vehicle_number: vehicleNumber,
      driver_name: driverName,
      driver_contact: driverContact,
      destination_type: destinationType,
      qc_required: destinationType === 'QC',
      status: initialStatus,
      total_items: itemsToReceive.length,
      entry_time: new Date(),
      checked_by: userId,
      checked_at: new Date(),
      remarks: `Created from Purchase Order: ${purchaseOrder.po_number}`,
      created_by: userId,
      updated_by: userId
    };

    const gateEntry = await db.GateEntry.create(gateEntryData, { transaction: txn });

    // Create Gate Entry Items
    const gateEntryItems = [];
    for (let i = 0; i < itemsToReceive.length; i++) {
      const item = itemsToReceive[i];
      const poItem = purchaseOrder.items.find(pi => pi.id === item.po_item_id);
      
      const receivedQty = parseFloat(item.received_quantity || 0);
      const unitPrice = parseFloat(item.unit_price || poItem?.unit_price || 0);
      const acceptedQty = destinationType === 'STORES' ? receivedQty : null; // QC items need approval

      const gateEntryItem = await db.GateEntryItem.create({
        gate_entry_id: gateEntry.id,
        po_item_id: item.po_item_id,
        line_number: i + 1,
        material_id: item.material_id,
        description: poItem?.material?.description || poItem?.description || null,
        po_quantity: item.po_quantity || parseFloat(poItem?.quantity || 0),
        received_quantity: receivedQty,
        accepted_quantity: acceptedQty,
        rejected_quantity: 0,
        uom_id: item.uom_id || poItem?.uom_id,
        unit_price: unitPrice,
        batch_number: item.batch_number || null,
        condition: item.condition || 'GOOD',
        qc_status: destinationType === 'QC' ? 'PENDING' : 'NOT_REQUIRED',
        qc_remarks: null,
        remarks: item.remarks || `From Purchase Order Item: ${poItem?.item_number || 'N/A'}`
      }, { transaction: txn });

      gateEntryItems.push(gateEntryItem);
    }

    if (shouldCommit) {
      await txn.commit();
    }

    // Reload Gate Entry with all associations
    const createdGateEntry = await db.GateEntry.findByPk(gateEntry.id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.FiscalYear, as: 'fiscalYear' },
        { model: db.BusinessPartner, as: 'vendor' },
        { model: db.PurchaseOrder, as: 'purchaseOrder' },
        {
          model: db.GateEntryItem,
          as: 'items',
          include: [
            { model: db.Material, as: 'material' },
            { model: db.UOM, as: 'uom' },
            { model: db.PurchaseOrderItem, as: 'purchaseOrderItem' }
          ]
        }
      ]
    });

    return createdGateEntry;
  } catch (error) {
    if (shouldCommit) {
      await txn.rollback();
    }
    throw error;
  }
};

/**
 * Generate Gate Entry number
 * Format: GE{YYYY}{####}
 */
async function generateGateEntryNumber(companyId, fiscalYearId, transaction) {
  const fiscalYear = await db.FiscalYear.findByPk(fiscalYearId, { transaction });
  if (!fiscalYear) {
    throw new Error('Fiscal Year not found');
  }

  const year = new Date(fiscalYear.start_date).getFullYear();
  const prefix = `GE${year}`;

  const lastGateEntry = await db.GateEntry.findOne({
    where: {
      company_id: companyId,
      fiscal_year_id: fiscalYearId,
      gate_entry_number: { [Op.like]: `${prefix}%` }
    },
    order: [['gate_entry_number', 'DESC']],
    transaction
  });

  let sequence = 1;
  if (lastGateEntry) {
    const lastSequence = parseInt(lastGateEntry.gate_entry_number.slice(-4)) || 0;
    sequence = lastSequence + 1;
  }

  return `${prefix}${sequence.toString().padStart(4, '0')}`;
}

