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

const toPositiveNumberOrNull = (value) => {
  if (value === null || value === undefined || value === '') return null;
  const parsed = parseFloat(value);
  if (!Number.isFinite(parsed) || parsed <= 0) return null;
  return parsed;
};

const getMaterialDefaultRate = async (materialId, plantId = null) => {
  const parsedMaterialId = Number(materialId);
  if (!Number.isInteger(parsedMaterialId) || parsedMaterialId <= 0) {
    return null;
  }

  const material = await db.Material.findByPk(parsedMaterialId, {
    attributes: ['id', 'unit_cost', 'last_purchase_rate'],
  });

  if (!material) {
    return null;
  }

  const directUnitCost = toPositiveNumberOrNull(material.unit_cost);
  if (directUnitCost !== null) return directUnitCost;

  const valuationWhere = {
    material_id: parsedMaterialId,
    status: 'ACTIVE',
  };
  let valuation = null;
  if (plantId) {
    valuation = await db.MaterialValuation.findOne({
      attributes: ['standard_price', 'moving_average_price'],
      where: { ...valuationWhere, plant_id: plantId },
      order: [['updated_at', 'DESC'], ['id', 'DESC']],
    });
  }
  if (!valuation) {
    valuation = await db.MaterialValuation.findOne({
      attributes: ['standard_price', 'moving_average_price'],
      where: valuationWhere,
      order: [['updated_at', 'DESC'], ['id', 'DESC']],
    });
  }
  if (valuation) {
    const standardPrice = toPositiveNumberOrNull(valuation.standard_price);
    if (standardPrice !== null) return standardPrice;
    const movingAveragePrice = toPositiveNumberOrNull(valuation.moving_average_price);
    if (movingAveragePrice !== null) return movingAveragePrice;
  }

  const plantWhere = {
    material_id: parsedMaterialId,
    status: 'ACTIVE',
  };
  let materialPlant = null;
  if (plantId) {
    materialPlant = await db.MaterialPlant.findOne({
      attributes: ['opening_balance_unit_cost'],
      where: { ...plantWhere, plant_id: plantId },
      order: [['updated_at', 'DESC'], ['id', 'DESC']],
    });
  }
  if (!materialPlant) {
    materialPlant = await db.MaterialPlant.findOne({
      attributes: ['opening_balance_unit_cost'],
      where: plantWhere,
      order: [['updated_at', 'DESC'], ['id', 'DESC']],
    });
  }
  const openingBalanceUnitCost = toPositiveNumberOrNull(materialPlant?.opening_balance_unit_cost);
  if (openingBalanceUnitCost !== null) return openingBalanceUnitCost;

  const preferredSupplier = await db.MaterialSupplier.findOne({
    attributes: ['standard_price'],
    where: {
      material_id: parsedMaterialId,
      status: 'ACTIVE',
      is_preferred: true,
    },
    order: [['updated_at', 'DESC'], ['id', 'DESC']],
  });
  const preferredSupplierCost = toPositiveNumberOrNull(preferredSupplier?.standard_price);
  if (preferredSupplierCost !== null) return preferredSupplierCost;

  const anySupplier = await db.MaterialSupplier.findOne({
    attributes: ['standard_price'],
    where: {
      material_id: parsedMaterialId,
      status: 'ACTIVE',
    },
    order: [['is_preferred', 'DESC'], ['updated_at', 'DESC'], ['id', 'DESC']],
  });
  const supplierCost = toPositiveNumberOrNull(anySupplier?.standard_price);
  if (supplierCost !== null) return supplierCost;

  const lastPurchaseRate = toPositiveNumberOrNull(material.last_purchase_rate);
  if (lastPurchaseRate !== null) {
    return lastPurchaseRate;
  }

  return null;
};

const getPlantIdFromBOM = async (bomId) => {
  const parsedBomId = Number(bomId);
  if (!Number.isInteger(parsedBomId) || parsedBomId <= 0) return null;
  const bom = await db.BOMHeader.findByPk(parsedBomId, { attributes: ['id', 'plant_id'] });
  if (!bom) return null;
  const parsedPlantId = Number(bom.plant_id);
  return Number.isInteger(parsedPlantId) && parsedPlantId > 0 ? parsedPlantId : null;
};

const resolveRateForPayload = async (payload) => {
  if (payload.rate !== undefined && payload.rate !== null && payload.rate !== '') {
    const explicitRate = parseFloat(payload.rate);
    if (Number.isFinite(explicitRate) && explicitRate > 0) {
      return explicitRate;
    }
  }

  const plantId = await getPlantIdFromBOM(payload.bom_id);
  const materialRate = await getMaterialDefaultRate(payload.material_id, plantId);
  return materialRate ?? 0;
};

exports.getAll = asyncHandler(async (req, res) => {
  const { page = 1, limit = 10, search, status, companyId, company_id, plantId, material_id } = req.query;
  const { limit: limitNum, offset } = getPagination(page, limit);

  const where = {};
  
  // BOMComponent doesn't have company_id or plant_id directly - they're on BOMHeader
  // So we'll filter through the include
  const bomHeaderWhere = {};
  
  // Support both camelCase and snake_case
  const companyIdValue = companyId || company_id;
  if (companyIdValue) {
    bomHeaderWhere.company_id = companyIdValue;
  } else if (req.user && !req.user.isSuperAdmin) {
    bomHeaderWhere.company_id = req.user.companyId;
  }

  if (plantId) {
    bomHeaderWhere.plant_id = plantId;
  }

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

  if (status) {
    where.status = status;
  } else {
    where.status = { [Op.ne]: 'DELETED' };
  }

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

  const records = await db.BOMComponent.findAndCountAll({
    where,
    limit: limitNum,
    offset,
    order: [['id', 'DESC']],
    include: [
      {
        model: db.BOMHeader,
        as: 'bom',
        where: Object.keys(bomHeaderWhere).length > 0 ? bomHeaderWhere : undefined,
        required: true,
        attributes: ['id', 'bom_number', 'material_id', 'bom_type', 'alternative', 'drawing_number', 'revision_number', 'status', 'company_id', 'plant_id'],
        include: [
          {
            model: db.Material,
            as: 'material',
            attributes: ['id', 'material_number', 'description'],
          },
          {
            model: db.Company,
            as: 'company',
            attributes: ['id', 'code', 'name'],
          },
          {
            model: db.Plant,
            as: 'plant',
            attributes: ['id', 'code', 'name'],
          },
        ],
      },
      {
        model: db.Material,
        as: 'material',
        attributes: ['id', 'material_number', 'description', 'material_type', 'material_type_id', 'material_group', 'material_group_id', 'drawing_file_path', 'drawing_file_name', 'drawing_file_type', 'unit_cost', 'last_purchase_rate'],
        include: [
          { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
          { model: db.MaterialGroup, as: 'materialGroup', attributes: ['id', 'group_code', 'group_name'] },
        ],
      },
      {
        model: db.UOM,
        as: 'componentUOM',
        attributes: ['id', 'uom_code', 'uom_name'],
      },
    ],
  });

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

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

  const record = await db.BOMComponent.findByPk(id, {
    include: [
      {
        model: db.BOMHeader,
        as: 'bom',
        attributes: ['id', 'bom_number', 'material_id', 'bom_type', 'alternative', 'drawing_number', 'revision_number'],
        include: [
          {
            model: db.Material,
            as: 'material',
            attributes: ['id', 'material_number', 'description'],
          },
        ],
      },
      {
        model: db.Material,
        as: 'material',
        attributes: ['id', 'material_number', 'description', 'material_type', 'material_type_id', 'material_group', 'material_group_id', 'drawing_file_path', 'drawing_file_name', 'drawing_file_type', 'unit_cost', 'last_purchase_rate'],
        include: [
          { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
          { model: db.MaterialGroup, as: 'materialGroup', attributes: ['id', 'group_code', 'group_name'] },
        ],
      },
      {
        model: db.UOM,
        as: 'componentUOM',
        attributes: ['id', 'uom_code', 'uom_name'],
      },
    ],
  });

  if (!record) {
    return sendResponse(res, 404, false, 'BOM Component not found', null);
  }

  sendResponse(res, 200, true, 'BOM Component retrieved successfully', record);
});

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

  data.created_by = userId;
  data.updated_by = userId;
  data.rate = await resolveRateForPayload(data);

  const record = await db.BOMComponent.create(data);

  // Fetch with associations
  const createdRecord = await db.BOMComponent.findByPk(record.id, {
    include: [
      {
        model: db.BOMHeader,
        as: 'bom',
        attributes: ['id', 'bom_number', 'material_id', 'bom_type', 'alternative', 'drawing_number', 'revision_number'],
      },
      {
        model: db.Material,
        as: 'material',
        attributes: ['id', 'material_number', 'description', 'material_type', 'material_type_id', 'material_group', 'material_group_id', 'drawing_file_path', 'drawing_file_name', 'drawing_file_type', 'unit_cost', 'last_purchase_rate'],
        include: [
          { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
          { model: db.MaterialGroup, as: 'materialGroup', attributes: ['id', 'group_code', 'group_name'] },
        ],
      },
      {
        model: db.UOM,
        as: 'componentUOM',
        attributes: ['id', 'uom_code', 'uom_name'],
      },
    ],
  });

  sendResponse(res, 201, true, 'BOM Component created successfully', createdRecord);
});

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

  const record = await db.BOMComponent.findByPk(id);
  if (!record) {
    return sendResponse(res, 404, false, 'BOM Component not found', null);
  }

  data.updated_by = userId;
  data.rate = await resolveRateForPayload({
    ...record.toJSON(),
    ...data,
    material_id: data.material_id ?? record.material_id,
  });

  await record.update(data);

  // Fetch with associations
  const updatedRecord = await db.BOMComponent.findByPk(id, {
    include: [
      {
        model: db.BOMHeader,
        as: 'bom',
        attributes: ['id', 'bom_number', 'material_id', 'bom_type', 'alternative', 'drawing_number', 'revision_number'],
      },
      {
        model: db.Material,
        as: 'material',
        attributes: ['id', 'material_number', 'description', 'material_type', 'material_type_id', 'material_group', 'material_group_id', 'drawing_file_path', 'drawing_file_name', 'drawing_file_type', 'unit_cost', 'last_purchase_rate'],
        include: [
          { model: db.MaterialType, as: 'materialType', attributes: ['id', 'type_code', 'type_name'] },
          { model: db.MaterialGroup, as: 'materialGroup', attributes: ['id', 'group_code', 'group_name'] },
        ],
      },
      {
        model: db.UOM,
        as: 'componentUOM',
        attributes: ['id', 'uom_code', 'uom_name'],
      },
    ],
  });

  sendResponse(res, 200, true, 'BOM Component updated successfully', updatedRecord);
});

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

  const record = await db.BOMComponent.findByPk(id);
  if (!record) {
    return sendResponse(res, 404, false, 'BOM Component not found', null);
  }

  // Soft delete by setting status to INACTIVE
  await record.update({
    status: 'INACTIVE',
    updated_by: userId,
  });

  sendResponse(res, 200, true, 'BOM Component deleted successfully', null);
});
