import mongoose from 'mongoose';
import { findAll as findAllPurchases, findById as findPurchaseById, create as createPurchaseDoc } from '../repositories/purchaseRepository.js';
import { findById as findProductById } from '../repositories/productRepository.js';
import { create as createStockHistory } from '../repositories/stockHistoryRepository.js';
import logAudit from '../utils/audit.js';
import { getVariant, syncProductFromVariants } from '../utils/variantUtils.js';

const getAllPurchases = async (query) => {
  return findAllPurchases(query);
};

const getPurchaseById = async (id) => {
  const purchase = await findPurchaseById(id);
  if (!purchase) throw Object.assign(new Error('Purchase not found'), { statusCode: 404 });
  return purchase;
};

const createPurchase = async (data, req) => {
  const session = await mongoose.startSession();
  session.startTransaction();

  try {
    const product = await findProductById(data.product);
    if (!product) throw Object.assign(new Error('Product not found'), { statusCode: 404 });

    let variantLabel = data.variantLabel || '';
    let sellingUnit = data.sellingUnit || 'pcs';
    const size = data.size || '';
    const previousStock = product.currentStock;

    if (product.variants && product.variants.length > 0) {
      const variant = getVariant(product, data.variant);
      if (!variant) {
        throw Object.assign(
          new Error(`Variant is required for ${product.name}`),
          { statusCode: 400 }
        );
      }
      variant.currentStock = (Number(variant.currentStock) || 0) + data.quantity;
      variantLabel = variantLabel || variant.label;
      sellingUnit = variant.sellingUnit || 'pcs';
      syncProductFromVariants(product);
    } else if (product.trackingType === 'SIZE' && data.size) {
      const variant = product.sizeVariants.find((v) => v.size === data.size);
      if (variant) {
        variant.stock += data.quantity;
      } else {
        product.sizeVariants.push({ size: data.size, stock: data.quantity, lowStockThreshold: product.lowStockThreshold || 5 });
      }
      product.currentStock = product.sizeVariants.reduce((sum, v) => sum + (v.stock || 0), 0);
    } else {
      product.currentStock = (Number(product.currentStock) || 0) + data.quantity;
    }

    const totalAmount = data.quantity * data.purchasePrice;
    const purchaseData = {
      ...data,
      variant: data.variant || '',
      variantLabel,
      sellingUnit,
      size,
      totalAmount,
    };
    const purchase = await createPurchaseDoc(purchaseData, session);

    await product.save({ session });

    await createStockHistory(
      {
        product: data.product,
        productName: product.name,
        type: 'PURCHASE',
        quantity: data.quantity,
        previousStock,
        newStock: product.currentStock,
        variant: data.variant || '',
        variantLabel,
        size,
        sellingUnit,
        reference: purchase._id.toString(),
      },
      session
    );

    await session.commitTransaction();
    logAudit(req, 'PURCHASE_CREATED', 'purchase', purchase._id, {
      productName: product.name,
      quantity: data.quantity,
      totalAmount,
    });
    return purchase;
  } catch (error) {
    await session.abortTransaction();
    throw error;
  } finally {
    session.endSession();
  }
};

export default { getAllPurchases, getPurchaseById, createPurchase };