const db = require('../models');
const { sendResponse, sendError } = require('../utils/response');
const asyncHandler = require('../middlewares/asyncHandler');
const { Op } = require('sequelize');

const toNumber = (value) => {
  const n = parseFloat(value);
  return Number.isFinite(n) ? n : 0;
};

// Get all QC pending gate entries
exports.getQCPendingEntries = asyncHandler(async (req, res) => {
  const { page = 1, limit = 25, search, companyId, plantId } = req.query;
  const { limit: limitNum, offset } = require('../utils/pagination').getPagination(page, limit);

  const where = {
    [Op.and]: [
      { [Op.or]: [{ qc_required: true }, { destination_type: 'QC' }] },
      { status: 'GRN_CREATED' },
      { grn_id: { [Op.ne]: null } }
    ]
  };
  
  if (companyId) {
    where.company_id = companyId;
  } else if (req.user && !req.user.isSuperAdmin) {
    where.company_id = req.user.companyId;
  }

  if (plantId) {
    where.plant_id = plantId;
  }

  if (search) {
    where[Op.and].push({
      [Op.or]: [
        { gate_entry_number: { [Op.like]: `%${search}%` } },
        { invoice_number: { [Op.like]: `%${search}%` } },
        { lr_number: { [Op.like]: `%${search}%` } }
      ]
    });
  }

  const records = await db.GateEntry.findAndCountAll({
    where,
    include: [
      { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
      { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
      { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      { model: db.PurchaseOrder, as: 'purchaseOrder', attributes: ['id', 'po_number'] },
      { model: db.GoodsReceiptNote, as: 'goodsReceiptNote', attributes: ['id', 'grn_number', 'grn_date'], required: false },
      {
        model: db.GateEntryItem,
        as: 'items',
        required: false,
        include: [
          {
            model: db.Material,
            as: 'material',
            include: [
              {
                model: db.MaterialThickness,
                as: 'materialThickness',
                attributes: ['id', 'thickness_value', 'thickness_unit'],
                required: false,
              },
              {
                model: db.MaterialGrade,
                as: 'materialGrade',
                attributes: ['id', 'grade_code', 'grade_name'],
                required: false,
              },
              { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
            ],
          },
          { model: db.UOM, as: 'uom' },
          { model: db.PurchaseOrderItem, as: 'purchaseOrderItem' }
        ]
      }
    ],
    limit: limitNum,
    offset,
    order: [['gate_entry_date', 'ASC'], ['id', 'ASC']]
  });

  const response = require('../utils/pagination').getPagingData(records, page, limitNum);
  // Prevent browser/proxy caching (avoids 304 with empty body in XHR/axios for some setups)
  res.setHeader('Cache-Control', 'no-store');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  sendResponse(res, 200, true, 'QC Pending Gate Entries retrieved successfully', response);
});

// Get QC pending gate entry by ID
exports.getQCPendingEntryById = asyncHandler(async (req, res) => {
  const { id } = req.params;

  const gateEntry = await db.GateEntry.findByPk(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.GoodsReceiptNote, as: 'goodsReceiptNote', attributes: ['id', 'grn_number', 'grn_date'], required: false },
      {
        model: db.GateEntryItem,
        as: 'items',
        include: [
          {
            model: db.Material,
            as: 'material',
            include: [
              {
                model: db.MaterialThickness,
                as: 'materialThickness',
                attributes: ['id', 'thickness_value', 'thickness_unit'],
                required: false,
              },
              {
                model: db.MaterialGrade,
                as: 'materialGrade',
                attributes: ['id', 'grade_code', 'grade_name'],
                required: false,
              },
              { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
              { model: db.UOM, as: 'baseUOM', attributes: ['id', 'uom_code', 'uom_name'] },
            ],
          },
          { model: db.UOM, as: 'uom' },
          { model: db.PurchaseOrderItem, as: 'purchaseOrderItem' },
          {
            model: db.GoodsReceiptNoteItem,
            as: 'grnItem',
            attributes: ['id', 'quantity', 'description'],
            required: false,
          },
        ],
        order: [['line_number', 'ASC']]
      }
    ]
  });

  if (!gateEntry) {
    return sendError(res, 'Gate Entry not found', 404);
  }

  if (!(gateEntry.qc_required || String(gateEntry.destination_type || '').toUpperCase() === 'QC')) {
    return sendError(res, 'Gate Entry does not require inward inspection', 400);
  }

  if (!gateEntry.grn_id || String(gateEntry.status || '').toUpperCase() !== 'GRN_CREATED') {
    return sendError(res, 'Inward inspection is available only after GRN creation', 400);
  }

  const plain = gateEntry.toJSON ? gateEntry.toJSON() : gateEntry;
  plain.items = (plain.items || []).map((item) => {
    const poQty = toNumber(item.po_quantity) || toNumber(item.purchaseOrderItem?.quantity) || toNumber(item.grnItem?.quantity);
    const receivedQty =
      toNumber(item.received_quantity)
      || toNumber(item.grnItem?.quantity)
      || (toNumber(item.accepted_quantity) + toNumber(item.rejected_quantity))
      || poQty
      || 0;
    return {
      ...item,
      po_quantity: poQty,
      received_quantity: receivedQty
    };
  });

  res.setHeader('Cache-Control', 'no-store');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  sendResponse(res, 200, true, 'QC Pending Gate Entry retrieved successfully', plain);
});

// Approve/Reject QC for gate entry items
exports.approveQC = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const { items, approval_status, rejection_reason } = req.body;
  const userId = req.user?.userId || null;

  if (!items || !Array.isArray(items) || items.length === 0) {
    return sendError(res, 'Items are required for QC approval', 400);
  }

  const gateEntry = await db.GateEntry.findByPk(id, {
    include: [{ model: db.GateEntryItem, as: 'items' }]
  });

  if (!gateEntry) {
    return sendError(res, 'Gate Entry not found', 404);
  }

  const hasPendingItems = Array.isArray(gateEntry.items)
    && gateEntry.items.some((item) => String(item?.qc_status || '').toUpperCase() === 'PENDING');
  if (!hasPendingItems) {
    return sendError(res, 'Gate Entry has no pending QC items', 400);
  }

  const transaction = await db.sequelize.transaction();

  try {
    let allApproved = true;
    let allRejected = true;
    let hasPartial = false;

    // Process each item
    for (const itemData of items) {
      const gateItem = await db.GateEntryItem.findByPk(itemData.item_id, { transaction });
      
      if (!gateItem || gateItem.gate_entry_id !== gateEntry.id) {
        await transaction.rollback();
        return sendError(res, `Invalid item ID: ${itemData.item_id}`, 400);
      }

      const rawReceivedQty = toNumber(gateItem.received_quantity);
      const poQty = toNumber(gateItem.po_quantity);
      const acceptedQty = parseFloat(itemData.accepted_quantity) || 0;
      const rejectedQty = parseFloat(itemData.rejected_quantity) || 0;
      const effectiveReceivedQty = rawReceivedQty > 0 ? rawReceivedQty : (poQty > 0 ? poQty : (acceptedQty + rejectedQty));

      // Validate quantities
      if (acceptedQty + rejectedQty > effectiveReceivedQty) {
        await transaction.rollback();
        return sendError(res, `Accepted + Rejected quantity cannot exceed received quantity for item ${gateItem.line_number}`, 400);
      }

      if (acceptedQty < 0 || rejectedQty < 0) {
        await transaction.rollback();
        return sendError(res, `Quantities cannot be negative for item ${gateItem.line_number}`, 400);
      }

      // Determine item QC status
      let itemQCStatus;
      if (acceptedQty > 0 && rejectedQty > 0) {
        itemQCStatus = 'APPROVED'; // Partial approval
        hasPartial = true;
        allApproved = false;
        allRejected = false;
      } else if (acceptedQty > 0) {
        itemQCStatus = 'APPROVED';
        allRejected = false;
      } else if (rejectedQty > 0) {
        itemQCStatus = 'REJECTED';
        allApproved = false;
      } else {
        itemQCStatus = 'PENDING';
        allApproved = false;
        allRejected = false;
      }

      // Update gate entry item
      await gateItem.update({
        received_quantity: effectiveReceivedQty,
        accepted_quantity: acceptedQty,
        rejected_quantity: rejectedQty,
        qc_status: itemQCStatus,
        qc_remarks: itemData.qc_remarks || null,
        qc_checked_by: userId,
        qc_checked_at: new Date()
      }, { transaction });
    }

    // Reload items to check their updated status
    const updatedItems = await db.GateEntryItem.findAll({
      where: { gate_entry_id: gateEntry.id },
      transaction
    });

    // Check if there are any approved items (accepted_quantity > 0)
    const hasApprovedItems = updatedItems.some(item => {
      const acceptedQty = parseFloat(item.accepted_quantity || 0);
      return acceptedQty > 0 && (item.qc_status === 'APPROVED' || item.qc_status === 'NOT_REQUIRED');
    });

    let newGateEntryStatus;
    const isGrnAlreadyCreated = !!gateEntry.grn_id || String(gateEntry.status || '').toUpperCase() === 'GRN_CREATED';
    if (allApproved) {
      // All items approved - ready for GRN
      newGateEntryStatus = isGrnAlreadyCreated ? 'GRN_CREATED' : 'GRN_READY';
      await gateEntry.update({
        status: newGateEntryStatus,
        qc_approved: true,
        qc_approved_at: new Date(),
        qc_approved_by: userId,
        qc_rejection_reason: null,
        updated_by: userId
      }, { transaction });
    } else if (allRejected) {
      // All items rejected
      newGateEntryStatus = 'QC_REJECTED';
      await gateEntry.update({
        status: 'QC_REJECTED',
        qc_approved: false,
        qc_approved_at: null,
        qc_approved_by: null,
        qc_rejection_reason: rejection_reason || 'All items rejected by QC',
        updated_by: userId
      }, { transaction });
    } else if (hasApprovedItems) {
      // Partial approval - some items approved, can proceed with approved items
      newGateEntryStatus = isGrnAlreadyCreated ? 'GRN_CREATED' : 'GRN_READY'; // Keep GRN_CREATED if already created
      await gateEntry.update({
        status: newGateEntryStatus,
        qc_approved: true,
        qc_approved_at: new Date(),
        qc_approved_by: userId,
        qc_rejection_reason: hasPartial ? 'Partial approval - some items rejected' : null,
        updated_by: userId
      }, { transaction });
    } else {
      // No approved items but not all rejected (shouldn't happen, but handle it)
      newGateEntryStatus = 'QC_PENDING';
      await gateEntry.update({
        status: 'QC_PENDING',
        updated_by: userId
      }, { transaction });
    }

    await transaction.commit();

    const updatedGateEntry = await db.GateEntry.findByPk(gateEntry.id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { 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.User, as: 'qcCheckedByUser', attributes: ['id', 'username', 'first_name', 'last_name'] }
          ]
        }
      ]
    });

    sendResponse(res, 200, true, `QC processed successfully. Gate Entry status: ${newGateEntryStatus}`, updatedGateEntry);
  } catch (error) {
    // Avoid "Transaction cannot be rolled back because it has been finished with state: commit"
    if (transaction && !transaction.finished) {
      await transaction.rollback();
    }
    console.error('Error in QC approval:', error);
    throw error;
  }
});

// Reject entire gate entry
exports.rejectQC = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const { rejection_reason } = req.body;
  const userId = req.user?.userId || null;

  if (!rejection_reason || rejection_reason.trim() === '') {
    return sendError(res, 'Rejection reason is required', 400);
  }

  const gateEntry = await db.GateEntry.findByPk(id, {
    include: [{ model: db.GateEntryItem, as: 'items' }]
  });

  if (!gateEntry) {
    return sendError(res, 'Gate Entry not found', 404);
  }

  const hasPendingItems = Array.isArray(gateEntry.items)
    && gateEntry.items.some((item) => String(item?.qc_status || '').toUpperCase() === 'PENDING');
  if (!hasPendingItems) {
    return sendError(res, 'Gate Entry has no pending QC items', 400);
  }

  const transaction = await db.sequelize.transaction();

  try {
    // Reject all pending items
    await db.GateEntryItem.update(
      {
        qc_status: 'REJECTED',
        accepted_quantity: 0,
        rejected_quantity: db.sequelize.col('received_quantity'),
        qc_remarks: rejection_reason,
        qc_checked_by: userId,
        qc_checked_at: new Date()
      },
      {
        where: {
          gate_entry_id: gateEntry.id,
          qc_status: 'PENDING'
        },
        transaction
      }
    );

    // Update gate entry
    await gateEntry.update({
      status: 'QC_REJECTED',
      qc_approved: false,
      qc_approved_at: null,
      qc_approved_by: null,
      qc_rejection_reason: rejection_reason,
      updated_by: userId
    }, { transaction });

    await transaction.commit();

    const updatedGateEntry = await db.GateEntry.findByPk(gateEntry.id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.BusinessPartner, as: 'vendor' },
        {
          model: db.GateEntryItem,
          as: 'items',
          include: [
            { model: db.Material, as: 'material' },
            { model: db.UOM, as: 'uom' }
          ]
        }
      ]
    });

    sendResponse(res, 200, true, 'Gate Entry rejected by QC successfully', updatedGateEntry);
  } catch (error) {
    if (transaction && !transaction.finished) {
      await transaction.rollback();
    }
    throw error;
  }
});

