const { Op } = require('sequelize');
const db = require('../models');
const asyncHandler = require('../middlewares/asyncHandler');
const { sendResponse, sendError } = require('../utils/response');
const { getPagination, getPagingData } = require('../utils/pagination');
const { generateNextNumber } = require('../services/numberFormatService');
const stockLedgerService = require('../services/stockLedgerService');

const getUserId = (req) => req.user?.userId || req.user?.id || null;
const DC_EDITABLE_STATUSES = ['DRAFT', 'APPROVED'];
const toNum = (v) => {
  const n = parseFloat(v);
  return Number.isFinite(n) ? n : 0;
};
const composeAddress = (addr) => [
  addr?.address_line1,
  addr?.address_line2,
  addr?.city?.city_name,
  addr?.state?.state_name,
  addr?.country?.country_name,
  addr?.pincode
]
  .map((x) => String(x || '').trim())
  .filter(Boolean)
  .join(', ');

const resolveCustomerPO = async ({ customerPOId, companyId, plantId, vendorId, transaction }) => {
  if (!customerPOId) return null;
  const po = await db.CustomerPO.findByPk(customerPOId, {
    attributes: ['id', 'po_number', 'company_id', 'plant_id', 'customer_id', 'status'],
    transaction
  });
  if (!po) throw new Error('Selected Customer PO not found');
  if (String(po.company_id) !== String(companyId)) throw new Error('Selected Customer PO does not belong to this company');
  if (plantId && String(po.plant_id) !== String(plantId)) throw new Error('Selected Customer PO does not belong to this plant');
  if (vendorId && String(po.customer_id) !== String(vendorId)) throw new Error('Selected Customer PO does not match selected vendor');
  if (!['APPROVED', 'RELEASED'].includes(String(po.status || '').toUpperCase())) {
    throw new Error('Selected Customer PO must be APPROVED or RELEASED');
  }
  return po;
};

const generateSubcontractDCNumber = async (companyId, fiscalYearId = null, transaction = null) => {
  try {
    return await generateNextNumber(companyId, 'SUBCONTRACT_DC', fiscalYearId, transaction);
  } catch (error) {
    const year = new Date().getFullYear();
    const prefix = `SDC${year}`;
    const last = await db.SubcontractDC.findOne({
      where: { company_id: companyId, dc_number: { [Op.like]: `${prefix}%` } },
      order: [['dc_number', 'DESC']],
      ...(transaction ? { transaction } : {})
    });
    const next = (parseInt(String(last?.dc_number || '').slice(-4), 10) || 0) + 1;
    return `${prefix}${String(next).padStart(4, '0')}`;
  }
};

const generateSubcontractReceiptNumber = async (companyId, fiscalYearId = null, transaction = null) => {
  try {
    return await generateNextNumber(companyId, 'SUBCONTRACT_RECEIPT', fiscalYearId, transaction);
  } catch (error) {
    const year = new Date().getFullYear();
    const prefix = `SRC${year}`;
    const last = await db.SubcontractReceipt.findOne({
      where: { company_id: companyId, receipt_number: { [Op.like]: `${prefix}%` } },
      order: [['receipt_number', 'DESC']],
      ...(transaction ? { transaction } : {})
    });
    const next = (parseInt(String(last?.receipt_number || '').slice(-4), 10) || 0) + 1;
    return `${prefix}${String(next).padStart(4, '0')}`;
  }
};

exports.getAllDC = asyncHandler(async (req, res) => {
  const { page = 1, limit = 25, search, companyId, plantId, fiscalYearId, vendorId, status } = req.query;
  const { limit: limitNum, offset } = getPagination(page, limit);
  const where = {};

  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 (fiscalYearId) where.fiscal_year_id = fiscalYearId;
  if (vendorId) where.vendor_id = vendorId;
  if (status) where.status = status;
  if (search) {
    where[Op.or] = [
      { dc_number: { [Op.like]: `%${search}%` } },
      { subcontract_po_ref: { [Op.like]: `%${search}%` } },
      { purpose: { [Op.like]: `%${search}%` } },
      { operation_type: { [Op.like]: `%${search}%` } }
    ];
  }

  const rows = await db.SubcontractDC.findAndCountAll({
    where,
    include: [
      { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number', 'po_date', 'customer_id'], required: false },
      { model: db.SubcontractDCItem, as: 'items', required: false, attributes: ['id'] }
    ],
    distinct: true,
    limit: limitNum,
    offset,
    order: [['dc_date', 'DESC'], ['id', 'DESC']]
  });

  sendResponse(res, 200, true, 'Subcontract DC list retrieved successfully', getPagingData(rows, page, limitNum));
});

exports.getDCById = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const dc = await db.SubcontractDC.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.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number', 'po_date', 'customer_id'], required: false },
      {
        model: db.SubcontractDCItem,
        as: 'items',
        include: [
          { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] },
          { model: db.UOM, as: 'uom', attributes: ['id', 'uom_code', 'uom_name'] },
          { model: db.StorageLocation, as: 'storageLocation', attributes: ['id', 'storage_location_code', 'description'] }
        ]
      }
    ],
    order: [[{ model: db.SubcontractDCItem, as: 'items' }, 'line_number', 'ASC']]
  });
  if (!dc) return sendError(res, 'Subcontract DC not found', 404);
  sendResponse(res, 200, true, 'Subcontract DC retrieved successfully', dc);
});

exports.createDC = asyncHandler(async (req, res) => {
  const userId = getUserId(req);
  const companyId = req.user?.companyId || req.body.company_id;
  const data = req.body || {};
  if (!companyId) return sendError(res, 'Company ID is required', 400);
  if (!data.plant_id || !data.fiscal_year_id || !data.vendor_id || !data.operation_type) {
    return sendError(res, 'Plant, Fiscal Year, Vendor and Operation Type are required', 400);
  }
  if (!Array.isArray(data.items) || data.items.length === 0) {
    return sendError(res, 'At least one material item is required', 400);
  }

  const transaction = await db.sequelize.transaction();
  try {
    const customerPO = await resolveCustomerPO({
      customerPOId: data.customer_po_id,
      companyId,
      plantId: data.plant_id,
      vendorId: data.vendor_id,
      transaction
    });
    const dcNumber = data.dc_number || await generateSubcontractDCNumber(companyId, data.fiscal_year_id, transaction);
    const dc = await db.SubcontractDC.create({
      company_id: companyId,
      plant_id: data.plant_id,
      fiscal_year_id: data.fiscal_year_id,
      dc_number: dcNumber,
      dc_date: data.dc_date || new Date().toISOString().slice(0, 10),
      vendor_id: data.vendor_id,
      customer_po_id: customerPO?.id || null,
      operation_type: data.operation_type,
      purpose: data.purpose || null,
      subcontract_po_ref: customerPO?.po_number || data.subcontract_po_ref || null,
      delivery_address: data.delivery_address || null,
      transport_name: data.transport_name || null,
      vehicle_no: data.vehicle_no || null,
      driver_name: data.driver_name || null,
      driver_phone: data.driver_phone || null,
      eway_bill_no: data.eway_bill_no || null,
      remarks: data.remarks || null,
      status: 'DRAFT',
      created_by: userId,
      updated_by: userId
    }, { transaction });

    let totalSentQty = 0;
    let totalAmount = 0;
    for (let i = 0; i < data.items.length; i += 1) {
      const item = data.items[i];
      const sentQty = toNum(item.sent_quantity || item.quantity);
      if (!(sentQty > 0)) throw new Error(`Sent quantity must be greater than 0 at line ${i + 1}`);
      const unitPrice = toNum(item.unit_price);
      const lineTotal = sentQty * unitPrice;

      const created = await db.SubcontractDCItem.create({
        subcontract_dc_id: dc.id,
        line_number: i + 1,
        material_id: item.material_id,
        description: item.description || null,
        sent_quantity: sentQty,
        unit_price: unitPrice,
        line_total: lineTotal,
        received_quantity: 0,
        rejection_quantity: 0,
        uom_id: item.uom_id,
        storage_location_id: item.storage_location_id || null,
        remarks: item.remarks || null
      }, { transaction });

      totalSentQty += sentQty;
      totalAmount += lineTotal;
    }

    await dc.update({ total_sent_qty: totalSentQty, total_amount: totalAmount }, { transaction });
    await transaction.commit();

    const createdDC = await db.SubcontractDC.findByPk(dc.id, {
      include: [
        { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
        { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number', 'po_date', 'customer_id'], required: false },
        {
          model: db.SubcontractDCItem,
          as: 'items',
          include: [
            { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] },
            { model: db.UOM, as: 'uom', attributes: ['id', 'uom_code'] }
          ]
        }
      ]
    });
    sendResponse(res, 201, true, 'Subcontract DC created successfully', createdDC);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to create Subcontract DC', 400);
  }
});

exports.updateDC = asyncHandler(async (req, res) => {
  const userId = getUserId(req);
  const { id } = req.params;
  const data = req.body || {};

  const transaction = await db.sequelize.transaction();
  try {
    const dc = await db.SubcontractDC.findByPk(id, {
      include: [{ model: db.SubcontractDCItem, as: 'items' }],
      transaction,
      lock: transaction.LOCK.UPDATE
    });
    if (!dc) throw new Error('Subcontract DC not found');
    if (!DC_EDITABLE_STATUSES.includes(dc.status)) {
      throw new Error(`Cannot edit Subcontract DC with status ${dc.status}`);
    }
    if (!Array.isArray(data.items) || data.items.length === 0) {
      throw new Error('At least one material item is required');
    }

    const nextVendorId = data.vendor_id || dc.vendor_id;
    const customerPO = await resolveCustomerPO({
      customerPOId: data.customer_po_id,
      companyId: dc.company_id,
      plantId: dc.plant_id,
      vendorId: nextVendorId,
      transaction
    });

    await dc.update({
      dc_date: data.dc_date || dc.dc_date,
      vendor_id: nextVendorId,
      customer_po_id: Object.prototype.hasOwnProperty.call(data, 'customer_po_id')
        ? (customerPO?.id || null)
        : dc.customer_po_id,
      operation_type: data.operation_type || dc.operation_type,
      purpose: data.purpose ?? dc.purpose,
      subcontract_po_ref: customerPO?.po_number || data.subcontract_po_ref || dc.subcontract_po_ref || null,
      delivery_address: data.delivery_address ?? dc.delivery_address,
      transport_name: data.transport_name ?? dc.transport_name,
      vehicle_no: data.vehicle_no ?? dc.vehicle_no,
      driver_name: data.driver_name ?? dc.driver_name,
      driver_phone: data.driver_phone ?? dc.driver_phone,
      eway_bill_no: data.eway_bill_no ?? dc.eway_bill_no,
      remarks: data.remarks || null,
      updated_by: userId
    }, { transaction });

    await db.SubcontractDCItem.destroy({ where: { subcontract_dc_id: dc.id }, transaction });

    let totalSentQty = 0;
    let totalAmount = 0;
    for (let i = 0; i < data.items.length; i += 1) {
      const item = data.items[i];
      const sentQty = toNum(item.sent_quantity || item.quantity);
      if (!(sentQty > 0)) throw new Error(`Sent quantity must be greater than 0 at line ${i + 1}`);
      const unitPrice = toNum(item.unit_price);
      const lineTotal = sentQty * unitPrice;

      await db.SubcontractDCItem.create({
        subcontract_dc_id: dc.id,
        line_number: i + 1,
        material_id: item.material_id,
        description: item.description || null,
        sent_quantity: sentQty,
        unit_price: unitPrice,
        line_total: lineTotal,
        received_quantity: 0,
        rejection_quantity: 0,
        uom_id: item.uom_id,
        storage_location_id: item.storage_location_id || null,
        remarks: item.remarks || null
      }, { transaction });

      totalSentQty += sentQty;
      totalAmount += lineTotal;
    }

    await dc.update({ total_sent_qty: totalSentQty, total_amount: totalAmount }, { transaction });
    await transaction.commit();

    const updated = await db.SubcontractDC.findByPk(dc.id, {
      include: [
        { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
        { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number', 'po_date', 'customer_id'], required: false },
        { model: db.SubcontractDCItem, as: 'items' }
      ]
    });
    sendResponse(res, 200, true, 'Subcontract DC updated successfully', updated);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to update Subcontract DC', 400);
  }
});

exports.approveDC = asyncHandler(async (req, res) => {
  const userId = getUserId(req);
  const { id } = req.params;
  const dc = await db.SubcontractDC.findByPk(id);
  if (!dc) return sendError(res, 'Subcontract DC not found', 404);
  if (dc.status !== 'DRAFT') return sendError(res, `Only DRAFT DC can be approved. Current: ${dc.status}`, 400);

  await dc.update({ status: 'APPROVED', updated_by: userId });
  sendResponse(res, 200, true, 'Subcontract DC approved successfully', dc);
});

exports.sendDC = asyncHandler(async (req, res) => {
  const userId = getUserId(req);
  const { id } = req.params;
  const transaction = await db.sequelize.transaction();
  try {
    const dc = await db.SubcontractDC.findByPk(id, {
      include: [{ model: db.SubcontractDCItem, as: 'items' }],
      transaction,
      lock: transaction.LOCK.UPDATE
    });
    if (!dc) throw new Error('Subcontract DC not found');
    if (dc.status !== 'APPROVED') throw new Error(`Only APPROVED DC can be sent. Current: ${dc.status}`);
    if (!Array.isArray(dc.items) || dc.items.length === 0) throw new Error('No items found to send');

    const locationIds = Array.from(
      new Set(
        (dc.items || [])
          .map((it) => parseInt(it.storage_location_id, 10))
          .filter((idVal) => Number.isFinite(idVal) && idVal > 0)
      )
    );
    const locationMap = new Map();
    if (locationIds.length > 0) {
      const locations = await db.StorageLocation.findAll({
        where: { id: { [Op.in]: locationIds } },
        attributes: ['id', 'storage_location_code'],
        transaction
      });
      locations.forEach((loc) => {
        locationMap.set(String(loc.id), loc.storage_location_code || String(loc.id));
      });
    }

    for (let i = 0; i < dc.items.length; i += 1) {
      const item = dc.items[i];
      const sentQty = parseFloat(item.sent_quantity || 0);
      if (!(sentQty > 0)) {
        throw new Error(`Sent quantity must be greater than 0 at line ${item.line_number || (i + 1)}`);
      }

      const locationCode = item.storage_location_id != null
        ? (locationMap.get(String(item.storage_location_id)) || String(item.storage_location_id))
        : null;

      await stockLedgerService.createLedgerEntry({
        company_id: dc.company_id,
        plant_id: dc.plant_id,
        material_id: item.material_id,
        transaction_date: dc.dc_date,
        transaction_type: 'ISSUE',
        reference_type: 'SUBCONTRACT_DC',
        reference_id: dc.id,
        reference_number: dc.dc_number,
        storage_location: locationCode,
        quantity_in: 0,
        quantity_out: sentQty,
        unit_price: 0,
        remarks: `Subcontract DC: ${dc.dc_number} | Operation: ${dc.operation_type}`
      }, transaction);
    }

    await dc.update({ status: 'SENT', updated_by: userId }, { transaction });
    await transaction.commit();
    sendResponse(res, 200, true, 'Subcontract DC sent successfully', dc);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to send Subcontract DC', 400);
  }
});

exports.getDCPendingItems = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const dc = await db.SubcontractDC.findByPk(id, {
    include: [
      {
        model: db.SubcontractDCItem,
        as: 'items',
        include: [
          { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] },
          { model: db.UOM, as: 'uom', attributes: ['id', 'uom_code', 'uom_name'] },
          { model: db.StorageLocation, as: 'storageLocation', attributes: ['id', 'storage_location_code', 'description'] }
        ]
      },
      { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number', 'po_date'], required: false }
    ],
    order: [[{ model: db.SubcontractDCItem, as: 'items' }, 'line_number', 'ASC']]
  });
  if (!dc) return sendError(res, 'Subcontract DC not found', 404);

  const pendingItems = (dc.items || []).map((item) => {
    const sent = parseFloat(item.sent_quantity || 0);
    const received = parseFloat(item.received_quantity || 0);
    const rejected = parseFloat(item.rejection_quantity || 0);
    const pending = Math.max(sent - received - rejected, 0);
    return {
      ...item.toJSON(),
      pending_quantity: pending
    };
  }).filter((item) => item.pending_quantity > 0);

  sendResponse(res, 200, true, 'Subcontract DC pending items retrieved successfully', {
    subcontract_dc_id: dc.id,
    dc_number: dc.dc_number,
    dc_date: dc.dc_date,
    vendor: dc.vendor,
    customer_po_id: dc.customer_po_id || null,
    customer_po_ref: dc.customerPO?.po_number || dc.subcontract_po_ref || null,
    purpose: dc.purpose || null,
    operation_type: dc.operation_type,
    items: pendingItems
  });
});

exports.getVendorAddress = asyncHandler(async (req, res) => {
  const vendorId = parseInt(req.params.vendorId, 10);
  if (!Number.isFinite(vendorId) || vendorId <= 0) return sendError(res, 'Valid vendorId is required', 400);

  const vendor = await db.BusinessPartner.findByPk(vendorId, {
    attributes: ['id', 'bp_number', 'name', 'legal_name'],
    include: [
      {
        model: db.BPAddress,
        as: 'addresses',
        required: false,
        include: [
          { model: db.City, as: 'city', attributes: ['id', 'city_name'], required: false },
          { model: db.State, as: 'state', attributes: ['id', 'state_name'], required: false },
          { model: db.Country, as: 'country', attributes: ['id', 'country_name'], required: false }
        ]
      }
    ]
  });
  if (!vendor) return sendError(res, 'Vendor not found', 404);

  const active = (vendor.addresses || []).filter((a) => String(a.status || 'ACTIVE').toUpperCase() === 'ACTIVE');
  const list = active.length ? active : (vendor.addresses || []);
  const preferred = list.find((a) => a.is_default)
    || list.find((a) => String(a.address_type || '').toUpperCase() === 'SHIPPING')
    || list.find((a) => String(a.address_type || '').toUpperCase() === 'BILLING')
    || list[0]
    || null;

  sendResponse(res, 200, true, 'Vendor address retrieved successfully', {
    vendor_id: vendor.id,
    vendor_name: vendor.name || vendor.legal_name || '',
    address: composeAddress(preferred),
    address_object: preferred
  });
});

exports.getAllReceipts = asyncHandler(async (req, res) => {
  const { page = 1, limit = 25, search, companyId, plantId, fiscalYearId, vendorId } = req.query;
  const { limit: limitNum, offset } = getPagination(page, limit);
  const where = {};

  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 (fiscalYearId) where.fiscal_year_id = fiscalYearId;
  if (vendorId) where.vendor_id = vendorId;
  if (search) {
    where[Op.or] = [
      { receipt_number: { [Op.like]: `%${search}%` } }
    ];
  }

  const rows = await db.SubcontractReceipt.findAndCountAll({
    where,
    include: [
      { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
      {
        model: db.SubcontractDC,
        as: 'subcontractDC',
        attributes: ['id', 'dc_number', 'dc_date', 'subcontract_po_ref', 'purpose', 'customer_po_id'],
        include: [
          { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number'], required: false }
        ]
      },
      { model: db.SubcontractReceiptItem, as: 'items', required: false, attributes: ['id'] }
    ],
    distinct: true,
    limit: limitNum,
    offset,
    order: [['receipt_date', 'DESC'], ['id', 'DESC']]
  });

  sendResponse(res, 200, true, 'Subcontract Receipts retrieved successfully', getPagingData(rows, page, limitNum));
});

exports.getReceiptById = asyncHandler(async (req, res) => {
  const { id } = req.params;
  const receipt = await db.SubcontractReceipt.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.SubcontractDC,
        as: 'subcontractDC',
        attributes: ['id', 'dc_number', 'dc_date', 'operation_type', 'subcontract_po_ref', 'purpose', 'customer_po_id'],
        include: [
          { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number'], required: false }
        ]
      },
      {
        model: db.SubcontractReceiptItem,
        as: 'items',
        include: [
          { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] },
          { model: db.UOM, as: 'uom', attributes: ['id', 'uom_code', 'uom_name'] },
          { model: db.StorageLocation, as: 'storageLocation', attributes: ['id', 'storage_location_code', 'description'] }
        ]
      }
    ],
    order: [[{ model: db.SubcontractReceiptItem, as: 'items' }, 'line_number', 'ASC']]
  });
  if (!receipt) return sendError(res, 'Subcontract Receipt not found', 404);
  sendResponse(res, 200, true, 'Subcontract Receipt retrieved successfully', receipt);
});

exports.createReceipt = asyncHandler(async (req, res) => {
  const userId = getUserId(req);
  const companyId = req.user?.companyId || req.body.company_id;
  const data = req.body || {};
  if (!companyId) return sendError(res, 'Company ID is required', 400);
  if (!data.plant_id || !data.fiscal_year_id || !data.subcontract_dc_id || !Array.isArray(data.items) || data.items.length === 0) {
    return sendError(res, 'Plant, Fiscal Year, Subcontract DC and receipt items are required', 400);
  }

  const transaction = await db.sequelize.transaction();
  try {
    const inputLocationIds = Array.from(
      new Set(
        (data.items || [])
          .map((it) => parseInt(it.storage_location_id, 10))
          .filter((idVal) => Number.isFinite(idVal) && idVal > 0)
      )
    );
    const locationMap = new Map();
    if (inputLocationIds.length > 0) {
      const locations = await db.StorageLocation.findAll({
        where: { id: { [Op.in]: inputLocationIds } },
        attributes: ['id', 'storage_location_code'],
        transaction
      });
      locations.forEach((loc) => {
        locationMap.set(String(loc.id), loc.storage_location_code || String(loc.id));
      });
    }

    const dc = await db.SubcontractDC.findByPk(data.subcontract_dc_id, {
      include: [{ model: db.SubcontractDCItem, as: 'items' }],
      transaction,
      lock: transaction.LOCK.UPDATE
    });
    if (!dc) throw new Error('Reference Subcontract DC not found');
    if (!['SENT', 'PARTIALLY_RECEIVED'].includes(dc.status)) {
      throw new Error(`Cannot receive against Subcontract DC with status ${dc.status}. Only SENT or PARTIALLY_RECEIVED is allowed`);
    }
    if (data.vendor_id && String(data.vendor_id) !== String(dc.vendor_id)) {
      throw new Error('Vendor does not match with selected Subcontract DC');
    }

    const receiptNumber = data.receipt_number || await generateSubcontractReceiptNumber(companyId, data.fiscal_year_id, transaction);
    const receipt = await db.SubcontractReceipt.create({
      company_id: companyId,
      plant_id: data.plant_id,
      fiscal_year_id: data.fiscal_year_id,
      receipt_number: receiptNumber,
      receipt_date: data.receipt_date || new Date().toISOString().slice(0, 10),
      vendor_id: dc.vendor_id,
      subcontract_dc_id: dc.id,
      remarks: data.remarks || null,
      status: 'POSTED',
      created_by: userId,
      updated_by: userId
    }, { transaction });

    let totalReceived = 0;
    let totalRejected = 0;

    const dcItemsById = new Map((dc.items || []).map((item) => [String(item.id), item]));
    for (let i = 0; i < data.items.length; i += 1) {
      const rItem = data.items[i];
      const dcItem = dcItemsById.get(String(rItem.subcontract_dc_item_id));
      if (!dcItem) throw new Error(`Invalid DC item at line ${i + 1}`);

      const sent = parseFloat(dcItem.sent_quantity || 0);
      const alreadyReceived = parseFloat(dcItem.received_quantity || 0);
      const alreadyRejected = parseFloat(dcItem.rejection_quantity || 0);
      const remaining = sent - alreadyReceived - alreadyRejected;

      const receivedQty = parseFloat(rItem.received_quantity || 0);
      const rejectedQty = parseFloat(rItem.rejection_quantity || 0);
      if (receivedQty < 0 || rejectedQty < 0) {
        throw new Error(`Received/Rejection quantity cannot be negative at line ${i + 1}`);
      }
      if (!(receivedQty > 0 || rejectedQty > 0)) {
        throw new Error(`Received or rejection quantity is required at line ${i + 1}`);
      }
      if (receivedQty + rejectedQty > remaining + 1e-9) {
        throw new Error(`Line ${i + 1} exceeds pending quantity. Pending: ${remaining.toFixed(3)}`);
      }

      await db.SubcontractReceiptItem.create({
        subcontract_receipt_id: receipt.id,
        subcontract_dc_item_id: dcItem.id,
        line_number: i + 1,
        material_id: dcItem.material_id,
        description: rItem.description || dcItem.description || null,
        sent_quantity: sent,
        received_quantity: receivedQty,
        rejection_quantity: rejectedQty,
        uom_id: dcItem.uom_id,
        storage_location_id: rItem.storage_location_id || dcItem.storage_location_id || null,
        remarks: rItem.remarks || null
      }, { transaction });

      if (receivedQty > 0) {
        const locationId = rItem.storage_location_id || dcItem.storage_location_id || null;
        const locationCode = locationId != null
          ? (locationMap.get(String(locationId)) || String(locationId))
          : null;
        await stockLedgerService.createLedgerEntry({
          company_id: companyId,
          plant_id: data.plant_id,
          material_id: dcItem.material_id,
          transaction_date: receipt.receipt_date,
          transaction_type: 'RECEIPT',
          reference_type: 'SUBCONTRACT_RECEIPT',
          reference_id: receipt.id,
          reference_number: receipt.receipt_number,
          // Stock ledger stores location as code/string, not numeric FK id.
          storage_location: locationCode,
          quantity_in: receivedQty,
          quantity_out: 0,
          unit_price: 0,
          remarks: `Subcontract Receipt: ${receipt.receipt_number} | DC: ${dc.dc_number}`
        }, transaction);
      }

      await dcItem.update({
        received_quantity: alreadyReceived + receivedQty,
        rejection_quantity: alreadyRejected + rejectedQty
      }, { transaction });

      totalReceived += receivedQty;
      totalRejected += rejectedQty;
    }

    await receipt.update({
      total_received_qty: totalReceived,
      total_rejection_qty: totalRejected
    }, { transaction });

    const refreshedItems = await db.SubcontractDCItem.findAll({
      where: { subcontract_dc_id: dc.id },
      transaction
    });
    const sentTotal = refreshedItems.reduce((sum, item) => sum + parseFloat(item.sent_quantity || 0), 0);
    const recTotal = refreshedItems.reduce((sum, item) => sum + parseFloat(item.received_quantity || 0), 0);
    const rejTotal = refreshedItems.reduce((sum, item) => sum + parseFloat(item.rejection_quantity || 0), 0);
    const isClosed = sentTotal <= (recTotal + rejTotal + 1e-9);

    await dc.update({
      total_sent_qty: sentTotal,
      total_received_qty: recTotal,
      status: isClosed ? 'CLOSED' : 'PARTIALLY_RECEIVED',
      updated_by: userId
    }, { transaction });

    await transaction.commit();

    const createdReceipt = await db.SubcontractReceipt.findByPk(receipt.id, {
      include: [
        { model: db.BusinessPartner, as: 'vendor', attributes: ['id', 'bp_number', 'name', 'legal_name'] },
        {
          model: db.SubcontractDC,
          as: 'subcontractDC',
          attributes: ['id', 'dc_number', 'status', 'operation_type', 'subcontract_po_ref', 'purpose', 'customer_po_id'],
          include: [
            { model: db.CustomerPO, as: 'customerPO', attributes: ['id', 'po_number'], required: false }
          ]
        },
        {
          model: db.SubcontractReceiptItem,
          as: 'items',
          include: [
            { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] },
            { model: db.UOM, as: 'uom', attributes: ['id', 'uom_code'] }
          ]
        }
      ]
    });

    sendResponse(res, 201, true, 'Subcontract Receipt created successfully', createdReceipt);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to create Subcontract Receipt', 400);
  }
});
