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

const generateScrapNumber = async (companyId) => {
  const today = new Date();
  const year = today.getFullYear();
  const prefix = `SR${year}`;
  
  const lastScrap = await db.ScrapReporting.findOne({
    where: {
      company_id: companyId,
      scrap_number: { [Op.like]: `${prefix}%` }
    },
    order: [['scrap_number', 'DESC']]
  });

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

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

exports.getAll = asyncHandler(async (req, res) => {
  const { page = 1, limit = 25, search, status, companyId, plantId, fiscalYearId, productionOrderId } = 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 (productionOrderId) {
    where.production_order_id = productionOrderId;
  }

  if (status) {
    where.status = status;
  }

  if (search) {
    where[Op.or] = [
      { scrap_number: { [Op.like]: `%${search}%` } }
    ];
  }

  const records = await db.ScrapReporting.findAndCountAll({
    where,
    include: [
      { model: db.Company, as: 'company', attributes: ['id', 'code', 'name'] },
      { model: db.Plant, as: 'plant', attributes: ['id', 'code', 'name'] },
      { model: db.ProductionOrder, as: 'productionOrder', attributes: ['id', 'production_order_number'] },
      { model: db.Material, as: 'material', attributes: ['id', 'material_number', 'description'] }
    ],
    limit: limitNum,
    offset,
    order: [['scrap_date', 'DESC'], ['id', 'DESC']]
  });

  const response = getPagingData(records, page, limitNum);
  sendResponse(res, 200, true, 'Scrap Reports retrieved successfully', response);
});

exports.getById = asyncHandler(async (req, res) => {
  const { id } = req.params;

  const scrap = await db.ScrapReporting.findByPk(id, {
    include: [
      { model: db.Company, as: 'company' },
      { model: db.Plant, as: 'plant' },
      { model: db.FiscalYear, as: 'fiscalYear' },
      { model: db.ProductionOrder, as: 'productionOrder' },
      { model: db.Material, as: 'material' },
      { model: db.UOM, as: 'uom' },
      { model: db.WorkCenter, as: 'workCenter' }
    ]
  });

  if (!scrap) {
    return sendError(res, 'Scrap Report not found', 404);
  }

  sendResponse(res, 200, true, 'Scrap Report retrieved successfully', scrap);
});

exports.create = asyncHandler(async (req, res) => {
  const data = req.body;
  const userId = req.user?.userId || null;
  const companyId = req.user?.companyId;

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

  if (!data.plant_id || !data.fiscal_year_id || !data.production_order_id || !data.material_id || !data.scrap_quantity) {
    return sendError(res, 'Plant ID, Fiscal Year ID, Production Order ID, Material ID, and Scrap Quantity are required', 400);
  }

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

  try {
    if (!data.scrap_number) {
      data.scrap_number = await generateScrapNumber(companyId);
    }

    data.company_id = companyId;
    data.created_by = userId;
    data.updated_by = userId;

    const scrap = await db.ScrapReporting.create(data, { transaction });

    if (data.status === 'POSTED') {
      const po = await db.ProductionOrder.findByPk(data.production_order_id);
      if (po) {
        const scrappedQty = parseFloat(po.scrapped_quantity) + parseFloat(data.scrap_quantity);
        await po.update({ scrapped_quantity: scrappedQty }, { transaction });
      }

      await scrap.update({
        status: 'POSTED',
        posted_at: new Date(),
        posted_by: userId
      }, { transaction });
    }

    await transaction.commit();

    const createdScrap = await db.ScrapReporting.findByPk(scrap.id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.ProductionOrder, as: 'productionOrder' },
        { model: db.Material, as: 'material' }
      ]
    });

    sendResponse(res, 201, true, 'Scrap Report created successfully', createdScrap);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message, 400);
  }
});

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

  const scrap = await db.ScrapReporting.findByPk(id);
  if (!scrap) {
    return sendError(res, 'Scrap Report not found', 404);
  }

  if (scrap.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Scrap Reports can be updated', 400);
  }

  data.updated_by = userId;
  await scrap.update(data);

  const updatedScrap = await db.ScrapReporting.findByPk(id, {
    include: [
      { model: db.Company, as: 'company' },
      { model: db.Plant, as: 'plant' },
      { model: db.ProductionOrder, as: 'productionOrder' },
      { model: db.Material, as: 'material' }
    ]
  });

  sendResponse(res, 200, true, 'Scrap Report updated successfully', updatedScrap);
});

exports.delete = asyncHandler(async (req, res) => {
  const { id } = req.params;

  const scrap = await db.ScrapReporting.findByPk(id);
  if (!scrap) {
    return sendError(res, 'Scrap Report not found', 404);
  }

  if (scrap.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Scrap Reports can be deleted', 400);
  }

  await scrap.update({ status: 'CANCELLED' });

  sendResponse(res, 200, true, 'Scrap Report cancelled successfully');
});

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

  const scrap = await db.ScrapReporting.findByPk(id);
  if (!scrap) {
    return sendError(res, 'Scrap Report not found', 404);
  }

  if (scrap.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Scrap Reports can be posted', 400);
  }

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

  try {
    const po = await db.ProductionOrder.findByPk(scrap.production_order_id);
    if (po) {
      const scrappedQty = parseFloat(po.scrapped_quantity) + parseFloat(scrap.scrap_quantity);
      await po.update({ scrapped_quantity: scrappedQty }, { transaction });
    }

    await scrap.update({
      status: 'POSTED',
      posted_at: new Date(),
      posted_by: userId
    }, { transaction });

    await transaction.commit();

    const updatedScrap = await db.ScrapReporting.findByPk(id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.ProductionOrder, as: 'productionOrder' },
        { model: db.Material, as: 'material' }
      ]
    });

    sendResponse(res, 200, true, 'Scrap Report posted successfully', updatedScrap);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message, 400);
  }
});

