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 finishedGoodsReceiptService = require('../services/finishedGoodsReceiptService');
const backflushingService = require('../services/backflushingService');
const productionOrderStatusService = require('../services/productionOrderStatusService');

const resolveAndValidateRoutingExecutionLink = async (data, transaction) => {
  const next = { ...data };

  const poId = next.production_order_id != null ? parseInt(next.production_order_id, 10) : null;
  const execId = next.production_order_routing_execution_id != null && next.production_order_routing_execution_id !== ''
    ? parseInt(next.production_order_routing_execution_id, 10)
    : null;
  const opNo = next.operation_number != null && next.operation_number !== ''
    ? parseInt(next.operation_number, 10)
    : null;
  const wcId = next.work_center_id != null && next.work_center_id !== ''
    ? parseInt(next.work_center_id, 10)
    : null;

  if (!poId) {
    return next;
  }

  // If execution id is provided: validate it belongs to the same production order.
  if (execId) {
    const exec = await db.ProductionOrderRoutingExecution.findOne({
      where: { id: execId, production_order_id: poId },
      transaction
    });
    if (!exec) {
      throw new Error('Invalid routing execution link for this Production Order');
    }

    // If user also provided work_center_id / operation_number, ensure consistency.
    if (opNo != null && parseInt(exec.operation_number, 10) !== opNo) {
      throw new Error('Operation number does not match the linked routing execution');
    }
    if (wcId != null && parseInt(exec.work_center_id, 10) !== wcId) {
      throw new Error('Work center does not match the linked routing execution');
    }

    next.production_order_routing_execution_id = exec.id;
    next.operation_number = exec.operation_number;
    next.work_center_id = exec.work_center_id;
    return next;
  }

  // Legacy / auto-link: if operation_number is provided, try to resolve the unique execution.
  if (opNo != null) {
    const exec = await db.ProductionOrderRoutingExecution.findOne({
      where: {
        production_order_id: poId,
        operation_number: opNo,
        ...(wcId != null ? { work_center_id: wcId } : {})
      },
      transaction
    });

    if (exec) {
      next.production_order_routing_execution_id = exec.id;
      // If work center not provided, fill from execution
      if (wcId == null) next.work_center_id = exec.work_center_id;
    }
  }

  return next;
};

const generateConfirmationNumber = async (companyId) => {
  const today = new Date();
  const year = today.getFullYear();
  const prefix = `PC${year}`;
  
  const lastConf = await db.ProductionConfirmation.findOne({
    where: {
      company_id: companyId,
      confirmation_number: { [Op.like]: `${prefix}%` }
    },
    order: [['confirmation_number', 'DESC']]
  });

  let sequence = 1;
  if (lastConf) {
    const lastSequence = parseInt(lastConf.confirmation_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] = [
      { confirmation_number: { [Op.like]: `%${search}%` } }
    ];
  }

  const records = await db.ProductionConfirmation.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.WorkCenter, as: 'workCenter', attributes: ['id', 'work_center_code', 'description'] },
      {
        model: db.ProductionOrderRoutingExecution,
        as: 'routingExecution',
        required: false,
        include: [
          { model: db.WorkCenter, as: 'workCenter', attributes: ['id', 'work_center_code', 'description'] },
          { model: db.RoutingOperation, as: 'routingOperation', attributes: ['id', 'operation_description', 'control_key'] }
        ]
      }
    ],
    limit: limitNum,
    offset,
    order: [['confirmation_date', 'DESC'], ['id', 'DESC']]
  });

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

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

  const conf = await db.ProductionConfirmation.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.WorkCenter, as: 'workCenter' },
      {
        model: db.ProductionOrderRoutingExecution,
        as: 'routingExecution',
        required: false,
        include: [
          { model: db.WorkCenter, as: 'workCenter' },
          { model: db.RoutingOperation, as: 'routingOperation' }
        ]
      },
      { model: db.UOM, as: 'uom' }
    ]
  });

  if (!conf) {
    return sendError(res, 'Production Confirmation not found', 404);
  }

  sendResponse(res, 200, true, 'Production Confirmation retrieved successfully', conf);
});

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.confirmed_quantity) {
    return sendError(res, 'Plant ID, Fiscal Year ID, Production Order ID, and Confirmed Quantity are required', 400);
  }

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

  try {
    if (!data.confirmation_number) {
      data.confirmation_number = await generateConfirmationNumber(companyId);
    }

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

    const resolvedData = await resolveAndValidateRoutingExecutionLink(data, transaction);
    const conf = await db.ProductionConfirmation.create(resolvedData, { transaction });

    if (resolvedData.status === 'POSTED') {
      const po = await db.ProductionOrder.findByPk(resolvedData.production_order_id, {
        include: [
          { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description'] }
        ],
        transaction
      });
      
      if (po) {
        const previousStatus = po.status;
        const confirmedQty = parseFloat(po.confirmed_quantity) + parseFloat(resolvedData.confirmed_quantity);
        let newStatus = po.status;
        
        if (confirmedQty >= parseFloat(po.order_quantity)) {
          newStatus = 'CONFIRMED';
        } else if (confirmedQty > 0) {
          newStatus = 'PARTIALLY_CONFIRMED';
        }

        await po.update({
          confirmed_quantity: confirmedQty,
          status: newStatus
        }, { transaction });

        // Auto-create Finished Goods Receipt when Production Order becomes CONFIRMED
        if (newStatus === 'CONFIRMED' && previousStatus !== 'CONFIRMED') {
          try {
            // Reload PO with updated confirmed_quantity
            await po.reload({ transaction });
            await finishedGoodsReceiptService.createFinishedGoodsReceipt(po, userId, transaction);
            console.log(`Auto-created Finished Goods Receipt for Production Order ${po.production_order_number}`);
          } catch (grnError) {
            console.error('Error creating finished goods receipt:', grnError);
            // Don't fail the confirmation if GRN creation fails - log and continue
            // This allows production confirmation to complete even if GRN creation has issues
          }
        }

        // Auto-backflush components with BACKFLUSH issue_type
        // Default trigger: Production Confirmation posting.
        // Optional trigger: OPERATION_COMPLETE (see routingExecutionService.completeOperation) via BACKFLUSH_TRIGGER env var.
        if (process.env.BACKFLUSH_TRIGGER !== 'OPERATION_COMPLETE') {
          try {
            const backflushIssue = await backflushingService.backflushComponents(
              po,
              parseFloat(resolvedData.confirmed_quantity),
              userId,
              transaction
            );
            if (backflushIssue) {
              console.log(`Auto-created Backflush MaterialIssue ${backflushIssue.issue_number} for Production Order ${po.production_order_number}`);
            }
          } catch (backflushError) {
            console.error('Error backflushing components:', backflushError);
            // Don't fail the confirmation if backflushing fails - log and continue
            // This allows production confirmation to complete even if backflushing has issues
          }
        }

        // Check if Production Order should be auto-closed
        // This happens after finished goods receipt and backflushing are complete
        if (newStatus === 'CONFIRMED') {
          try {
            // Reload PO to get latest data
            await po.reload({ transaction });
            await productionOrderStatusService.onProductionConfirmed(po, userId, transaction);
            // Reload again to get updated status if it changed to CLOSED
            await po.reload({ transaction });
          } catch (statusError) {
            console.error('Error checking Production Order completion status:', statusError);
            // Don't fail the confirmation if status check fails
          }
        }
      }

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

    await transaction.commit();

    const createdConf = await db.ProductionConfirmation.findByPk(conf.id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.ProductionOrder, as: 'productionOrder' },
        { model: db.WorkCenter, as: 'workCenter' },
        {
          model: db.ProductionOrderRoutingExecution,
          as: 'routingExecution',
          required: false,
          include: [
            { model: db.WorkCenter, as: 'workCenter' },
            { model: db.RoutingOperation, as: 'routingOperation' }
          ]
        }
      ]
    });

    sendResponse(res, 201, true, 'Production Confirmation created successfully', createdConf);
  } 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 conf = await db.ProductionConfirmation.findByPk(id);
  if (!conf) {
    return sendError(res, 'Production Confirmation not found', 404);
  }

  if (conf.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Production Confirmations can be updated', 400);
  }

  const transaction = await db.sequelize.transaction();
  try {
    const nextData = await resolveAndValidateRoutingExecutionLink(
      { ...data, production_order_id: data.production_order_id ?? conf.production_order_id },
      transaction
    );
    nextData.updated_by = userId;
    await conf.update(nextData, { transaction });
    await transaction.commit();
  } catch (error) {
    await transaction.rollback();
    return sendError(res, error.message, 400);
  }

  const updatedConf = await db.ProductionConfirmation.findByPk(id, {
    include: [
      { model: db.Company, as: 'company' },
      { model: db.Plant, as: 'plant' },
      { model: db.ProductionOrder, as: 'productionOrder' },
      { model: db.WorkCenter, as: 'workCenter' },
      {
        model: db.ProductionOrderRoutingExecution,
        as: 'routingExecution',
        required: false,
        include: [
          { model: db.WorkCenter, as: 'workCenter' },
          { model: db.RoutingOperation, as: 'routingOperation' }
        ]
      }
    ]
  });

  sendResponse(res, 200, true, 'Production Confirmation updated successfully', updatedConf);
});

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

  const conf = await db.ProductionConfirmation.findByPk(id);
  if (!conf) {
    return sendError(res, 'Production Confirmation not found', 404);
  }

  if (conf.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Production Confirmations can be deleted', 400);
  }

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

  sendResponse(res, 200, true, 'Production Confirmation cancelled successfully');
});

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

  const conf = await db.ProductionConfirmation.findByPk(id);
  if (!conf) {
    return sendError(res, 'Production Confirmation not found', 404);
  }

  if (conf.status !== 'DRAFT') {
    return sendError(res, 'Only DRAFT Production Confirmations can be posted', 400);
  }

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

  try {
    const po = await db.ProductionOrder.findByPk(conf.production_order_id, {
      include: [
        { model: db.Material, as: 'finishedMaterial', attributes: ['id', 'material_number', 'description'] }
      ],
      transaction
    });
    
    if (po) {
      const previousStatus = po.status;
      const confirmedQty = parseFloat(po.confirmed_quantity) + parseFloat(conf.confirmed_quantity);
      let newStatus = po.status;
      
      if (confirmedQty >= parseFloat(po.order_quantity)) {
        newStatus = 'CONFIRMED';
      } else if (confirmedQty > 0) {
        newStatus = 'PARTIALLY_CONFIRMED';
      }

      await po.update({
        confirmed_quantity: confirmedQty,
        status: newStatus
      }, { transaction });

      // Auto-create Finished Goods Receipt when Production Order becomes CONFIRMED
      if (newStatus === 'CONFIRMED' && previousStatus !== 'CONFIRMED') {
        try {
          // Reload PO with updated confirmed_quantity
          await po.reload({ transaction });
          await finishedGoodsReceiptService.createFinishedGoodsReceipt(po, userId, transaction);
          console.log(`Auto-created Finished Goods Receipt for Production Order ${po.production_order_number}`);
        } catch (grnError) {
          console.error('Error creating finished goods receipt:', grnError);
          // Don't fail the confirmation if GRN creation fails - log and continue
          // This allows production confirmation to complete even if GRN creation has issues
        }
      }

      // Auto-backflush components with BACKFLUSH issue_type
      // Default trigger: Production Confirmation posting.
      // Optional trigger: OPERATION_COMPLETE (see routingExecutionService.completeOperation) via BACKFLUSH_TRIGGER env var.
      if (process.env.BACKFLUSH_TRIGGER !== 'OPERATION_COMPLETE') {
        try {
          const backflushIssue = await backflushingService.backflushComponents(
            po,
            parseFloat(conf.confirmed_quantity),
            userId,
            transaction
          );
          if (backflushIssue) {
            console.log(`Auto-created Backflush MaterialIssue ${backflushIssue.issue_number} for Production Order ${po.production_order_number}`);
          }
        } catch (backflushError) {
          console.error('Error backflushing components:', backflushError);
          // Don't fail the confirmation if backflushing fails - log and continue
          // This allows production confirmation to complete even if backflushing has issues
        }
      }

      // Check if Production Order should be auto-closed
      // This happens after finished goods receipt and backflushing are complete
      if (newStatus === 'CONFIRMED') {
        try {
          // Reload PO to get latest data
          await po.reload({ transaction });
          await productionOrderStatusService.onProductionConfirmed(po, userId, transaction);
          // Reload again to get updated status if it changed to CLOSED
          await po.reload({ transaction });
        } catch (statusError) {
          console.error('Error checking Production Order completion status:', statusError);
          // Don't fail the confirmation if status check fails
        }
      }
    }

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

    await transaction.commit();

    const updatedConf = await db.ProductionConfirmation.findByPk(id, {
      include: [
        { model: db.Company, as: 'company' },
        { model: db.Plant, as: 'plant' },
        { model: db.ProductionOrder, as: 'productionOrder' },
        { model: db.WorkCenter, as: 'workCenter' }
      ]
    });

    sendResponse(res, 200, true, 'Production Confirmation posted successfully', updatedConf);
  } catch (error) {
    await transaction.rollback();
    sendError(res, error.message, 400);
  }
});

