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

/**
 * Calculate standard cost for a Production Order
 * POST /api/v1/production/costing/calculate-standard/:productionOrderId
 */
exports.calculateStandardCost = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;
  const userId = req.user?.userId || null;

  const productionOrder = await db.ProductionOrder.findByPk(productionOrderId);
  if (!productionOrder) {
    return sendError(res, 'Production Order not found', 404);
  }

  const costRecord = await productionCostingService.calculateStandardCost(productionOrder, userId);

  sendResponse(res, 200, true, 'Standard cost calculated successfully', costRecord);
});

/**
 * Calculate actual cost for a Production Order
 * POST /api/v1/production/costing/calculate-actual/:productionOrderId
 */
exports.calculateActualCost = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;
  const userId = req.user?.userId || null;

  const productionOrder = await db.ProductionOrder.findByPk(productionOrderId);
  if (!productionOrder) {
    return sendError(res, 'Production Order not found', 404);
  }

  const costRecord = await productionCostingService.calculateActualCost(productionOrder, userId);

  sendResponse(res, 200, true, 'Actual cost calculated successfully', costRecord);
});

/**
 * Calculate cost variance for a Production Order
 * POST /api/v1/production/costing/calculate-variance/:productionOrderId
 */
exports.calculateCostVariance = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;
  const userId = req.user?.userId || null;

  const productionOrder = await db.ProductionOrder.findByPk(productionOrderId);
  if (!productionOrder) {
    return sendError(res, 'Production Order not found', 404);
  }

  try {
    const varianceRecord = await productionCostingService.calculateCostVariance(productionOrder, userId);
    sendResponse(res, 200, true, 'Cost variance calculated successfully', varianceRecord);
  } catch (error) {
    return sendError(res, error.message, 400);
  }
});

/**
 * Get production cost for a Production Order
 * GET /api/v1/production/costing/:productionOrderId
 */
exports.getProductionCost = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;
  const { costType } = req.query;

  const cost = await productionCostingService.getProductionCost(productionOrderId, costType || null);

  if (costType && !cost) {
    return sendError(res, `${costType} cost not found`, 404);
  }

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

/**
 * Get cost variance for a Production Order
 * GET /api/v1/production/costing/:productionOrderId/variance
 */
exports.getCostVariance = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;

  const variance = await productionCostingService.getCostVariance(productionOrderId);

  if (!variance) {
    return sendError(res, 'Cost variance not found', 404);
  }

  sendResponse(res, 200, true, 'Cost variance retrieved successfully', variance);
});

/**
 * Calculate all costs (standard, actual, and variance) for a Production Order
 * POST /api/v1/production/costing/calculate-all/:productionOrderId
 */
exports.calculateAllCosts = asyncHandler(async (req, res) => {
  const { productionOrderId } = req.params;
  const userId = req.user?.userId || null;

  const productionOrder = await db.ProductionOrder.findByPk(productionOrderId);
  if (!productionOrder) {
    return sendError(res, 'Production Order not found', 404);
  }

  const results = {};

  try {
    // Calculate standard cost
    results.standardCost = await productionCostingService.calculateStandardCost(productionOrder, userId);
  } catch (error) {
    results.standardCostError = error.message;
  }

  try {
    // Calculate actual cost
    results.actualCost = await productionCostingService.calculateActualCost(productionOrder, userId);
  } catch (error) {
    results.actualCostError = error.message;
  }

  try {
    // Calculate variance
    if (results.standardCost && results.actualCost) {
      results.variance = await productionCostingService.calculateCostVariance(productionOrder, userId);
    }
  } catch (error) {
    results.varianceError = error.message;
  }

  sendResponse(res, 200, true, 'All costs calculated successfully', results);
});

/**
 * Estimate production cost for a Finished Good (material)
 * GET /api/v1/production/costing/estimate?material_id=1&plant_id=1
 */
exports.estimateByFinishedGood = asyncHandler(async (req, res) => {
  const materialId = parseInt(req.query.material_id, 10);
  const plantId = req.query.plant_id ? parseInt(req.query.plant_id, 10) : null;
  const companyId = req.user?.companyId || req.query.company_id;

  if (!Number.isInteger(materialId) || materialId <= 0) {
    return sendError(res, 'Valid material_id is required', 400);
  }

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

  const estimation = await productionCostingService.estimateCostByFinishedGood({
    companyId: Number(companyId),
    materialId,
    plantId: Number.isInteger(plantId) && plantId > 0 ? plantId : null
  });

  sendResponse(res, 200, true, 'Production cost estimated successfully', estimation);
});

/**
 * Save production cost estimation for a Finished Good in production_costing table
 * POST /api/v1/production/costing/estimate/save
 */
exports.saveEstimateByFinishedGood = asyncHandler(async (req, res) => {
  const materialId = parseInt(req.body?.material_id, 10);
  const plantId = req.body?.plant_id ? parseInt(req.body.plant_id, 10) : null;
  const remarks = req.body?.remarks || null;
  const companyId = req.user?.companyId || req.body?.company_id;
  const userId = req.user?.userId || null;

  if (!Number.isInteger(materialId) || materialId <= 0) {
    return sendError(res, 'Valid material_id is required', 400);
  }

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

  const saved = await productionCostingService.saveEstimatedCostByFinishedGood({
    companyId: Number(companyId),
    materialId,
    plantId: Number.isInteger(plantId) && plantId > 0 ? plantId : null,
    userId,
    remarks
  });

  sendResponse(res, 201, true, 'Production costing saved successfully', saved);
});

/**
 * Update BOM component rates with latest material rates and return refreshed estimate.
 * POST /api/v1/production/costing/estimate/update-bom-rates
 */
exports.updateBomRatesByFinishedGood = asyncHandler(async (req, res) => {
  const materialId = parseInt(req.body?.material_id, 10);
  const plantId = req.body?.plant_id ? parseInt(req.body.plant_id, 10) : null;
  const companyId = req.user?.companyId || req.body?.company_id;
  const userId = req.user?.userId || null;

  if (!Number.isInteger(materialId) || materialId <= 0) {
    return sendError(res, 'Valid material_id is required', 400);
  }

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

  const result = await productionCostingService.updateBomRatesWithLatestByFinishedGood({
    companyId: Number(companyId),
    materialId,
    plantId: Number.isInteger(plantId) && plantId > 0 ? plantId : null,
    userId
  });

  sendResponse(res, 200, true, 'BOM rates updated with latest material prices', result);
});

/**
 * Get saved production costing records
 * GET /api/v1/production/costing/estimate/saved?page=1&limit=25&search=&plant_id=
 */
exports.getSavedEstimateByFinishedGood = asyncHandler(async (req, res) => {
  const page = parseInt(req.query.page, 10) || 1;
  const limit = parseInt(req.query.limit, 10) || 25;
  const plantId = req.query.plant_id ? parseInt(req.query.plant_id, 10) : null;
  const search = req.query.search || '';
  const companyId = req.user?.companyId || req.query.company_id;
  const userId = req.user?.userId || null;

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

  const result = await productionCostingService.getSavedEstimatedCostings({
    companyId: Number(companyId),
    plantId: Number.isInteger(plantId) && plantId > 0 ? plantId : null,
    userId,
    page,
    limit,
    search
  });

  sendResponse(res, 200, true, 'Saved production costing records retrieved successfully', result);
});

