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

const generateIPMRNumber = async (companyId, transaction = null) => {
  const year = new Date().getFullYear();
  const prefix = `IPMR${year}`;
  const last = await db.InProgressMaterialRequest.findOne({
    where: {
      company_id: companyId,
      ipmr_number: { [Op.like]: `${prefix}%` },
    },
    order: [['ipmr_number', 'DESC']],
    ...(transaction ? { transaction } : {}),
  });
  let seq = 1;
  if (last?.ipmr_number) {
    seq = (parseInt(String(last.ipmr_number).slice(-4), 10) || 0) + 1;
  }
  return `${prefix}${String(seq).padStart(4, '0')}`;
};

exports.getAll = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const plantId = req.query.plantId || req.user?.plantId;
  const { production_order_id, search } = req.query;

  if (!companyId) return sendError(res, 'Company ID is required', 400);

  const where = { company_id: companyId };
  if (plantId) where.plant_id = plantId;
  if (production_order_id) where.production_order_id = production_order_id;
  if (search) {
    where[Op.or] = [
      { ipmr_number: { [Op.like]: `%${search}%` } },
      { reference_production_no: { [Op.like]: `%${search}%` } },
      { material_request_number: { [Op.like]: `%${search}%` } },
    ];
  }

  const rows = await db.InProgressMaterialRequest.findAll({
    where,
    include: [
      { model: db.InProgressMaterialRequestItem, as: 'items', required: false },
      { model: db.ProductionOrder, as: 'productionOrder', attributes: ['id', 'production_order_number'], required: false },
    ],
    order: [['id', 'DESC']],
  });

  sendResponse(res, 200, true, 'In-progress material requests fetched successfully', rows);
});

exports.getById = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const { id } = req.params;
  if (!companyId) return sendError(res, 'Company ID is required', 400);

  const row = await db.InProgressMaterialRequest.findOne({
    where: { id, company_id: companyId },
    include: [
      { model: db.InProgressMaterialRequestItem, as: 'items', required: false },
      { model: db.ProductionOrder, as: 'productionOrder', attributes: ['id', 'production_order_number'], required: false },
    ],
  });
  if (!row) return sendError(res, 'In-progress material request not found', 404);
  sendResponse(res, 200, true, 'In-progress material request fetched successfully', row);
});

exports.create = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;
  const {
    plant_id,
    fiscal_year_id,
    production_order_id,
    customer_po_id,
    reference_production_no,
    item_code,
    item_name,
    customer_name,
    order_qty,
    required_date,
    material_request_id,
    material_request_number,
    status,
    remarks,
    items = [],
  } = req.body;

  if (!companyId) return sendError(res, 'Company ID is required', 400);
  if (!plant_id || !fiscal_year_id || !production_order_id) {
    return sendError(res, 'plant_id, fiscal_year_id and production_order_id are required', 400);
  }
  if (!Array.isArray(items) || items.length === 0) {
    return sendError(res, 'At least one item is required', 400);
  }

  const transaction = await db.sequelize.transaction();
  try {
    const ipmrNumber = await generateIPMRNumber(companyId, transaction);
    const header = await db.InProgressMaterialRequest.create({
      company_id: companyId,
      plant_id,
      fiscal_year_id,
      production_order_id,
      customer_po_id: customer_po_id || null,
      ipmr_number: ipmrNumber,
      reference_production_no: reference_production_no || null,
      item_code: item_code || null,
      item_name: item_name || null,
      customer_name: customer_name || null,
      order_qty: order_qty || 0,
      required_date: required_date || null,
      material_request_id: material_request_id || null,
      material_request_number: material_request_number || null,
      status: status || 'DRAFT',
      remarks: remarks || null,
      created_by: userId,
      updated_by: userId,
    }, { transaction });

    let line = 1;
    for (const it of items) {
      await db.InProgressMaterialRequestItem.create({
        in_progress_request_id: header.id,
        line_number: line++,
        material_id: it.material_id,
        material_number: it.material_number || null,
        description: it.description || null,
        uom_code: it.uom_code || null,
        store_code: it.store_code || null,
        required_quantity: it.required_quantity || 0,
        available_quantity: it.available_quantity || 0,
        shortage_quantity: it.shortage_quantity || 0,
        status: it.status || 'SENT_TO_MR',
        remarks: it.remarks || null,
      }, { transaction });
    }

    await transaction.commit();

    const created = await db.InProgressMaterialRequest.findByPk(header.id, {
      include: [{ model: db.InProgressMaterialRequestItem, as: 'items', required: false }],
    });
    sendResponse(res, 201, true, 'In-progress material request saved successfully', created);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to save in-progress material request', 500);
  }
});

exports.update = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;
  const { id } = req.params;
  const {
    reference_production_no,
    required_date,
    item_code,
    item_name,
    customer_name,
    order_qty,
    remarks,
    items,
  } = req.body;

  if (!companyId) return sendError(res, 'Company ID is required', 400);
  const row = await db.InProgressMaterialRequest.findOne({ where: { id, company_id: companyId } });
  if (!row) return sendError(res, 'In-progress material request not found', 404);

  const transaction = await db.sequelize.transaction();
  try {
    await row.update({
      reference_production_no: reference_production_no ?? row.reference_production_no,
      required_date: required_date ?? row.required_date,
      item_code: item_code ?? row.item_code,
      item_name: item_name ?? row.item_name,
      customer_name: customer_name ?? row.customer_name,
      order_qty: order_qty ?? row.order_qty,
      remarks: remarks ?? row.remarks,
      updated_by: userId,
    }, { transaction });

    if (Array.isArray(items) && items.length > 0) {
      await db.InProgressMaterialRequestItem.destroy({
        where: { in_progress_request_id: row.id },
        transaction,
      });
      let line = 1;
      for (const it of items) {
        await db.InProgressMaterialRequestItem.create({
          in_progress_request_id: row.id,
          line_number: line++,
          material_id: it.material_id,
          material_number: it.material_number || null,
          description: it.description || null,
          uom_code: it.uom_code || null,
          store_code: it.store_code || null,
          required_quantity: it.required_quantity || 0,
          available_quantity: it.available_quantity || 0,
          shortage_quantity: it.shortage_quantity || 0,
          status: it.status || 'SENT_TO_MR',
          remarks: it.remarks || null,
        }, { transaction });
      }
    }

    await transaction.commit();
    const updated = await db.InProgressMaterialRequest.findByPk(row.id, {
      include: [{ model: db.InProgressMaterialRequestItem, as: 'items', required: false }],
    });
    sendResponse(res, 200, true, 'In-progress material request updated successfully', updated);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message || 'Failed to update in-progress material request', 500);
  }
});

exports.updateStatus = asyncHandler(async (req, res) => {
  const companyId = req.user?.companyId;
  const userId = req.user?.userId || null;
  const { id } = req.params;
  const { status } = req.body;

  if (!companyId) return sendError(res, 'Company ID is required', 400);
  const allowedStatuses = ['DRAFT', 'SUBMITTED', 'APPROVED', 'SENT_TO_MR', 'PARTIAL', 'COMPLETED', 'CANCELLED'];
  if (!allowedStatuses.includes(String(status || '').toUpperCase())) {
    return sendError(res, 'Invalid status', 400);
  }

  const row = await db.InProgressMaterialRequest.findOne({
    where: { id, company_id: companyId },
  });
  if (!row) return sendError(res, 'In-progress material request not found', 404);

  const current = String(row.status || '').toUpperCase();
  const next = String(status || '').toUpperCase();
  // Basic workflow control
  if (next === 'APPROVED' && !['SUBMITTED', 'SENT_TO_MR'].includes(current)) {
    return sendError(res, 'Only SUBMITTED/SENT_TO_MR request can be approved', 400);
  }
  if (next === 'SUBMITTED' && !['DRAFT'].includes(current)) {
    return sendError(res, 'Only DRAFT request can be submitted', 400);
  }

  await row.update({ status: next, updated_by: userId });
  sendResponse(res, 200, true, 'Status updated successfully', row);
});
