const salesRepo = require('../repositories/salesRepository');
const productRepo = require('../repositories/productRepository');
const packageRepo = require('../repositories/packageRepository');
const { getClient } = require('../config/database');
const { successResponse, errorResponse, paginate, generateInvoiceNo } = require('../utils/helpers');
const { logAudit } = require('../middleware/auditLog');

async function getAll(req, res, next) {
  try {
    const { page, limit, search, startDate, endDate, paymentMethod } = req.query;
    const { limit: l, offset, page: p } = paginate(page, limit);
    const { rows, total } = await salesRepo.findAll({ limit: l, offset, search, startDate, endDate, paymentMethod });
    return successResponse(res, { data: rows, total, page: p, limit: l, totalPages: Math.ceil(total / l) });
  } catch (err) { next(err); }
}

async function getById(req, res, next) {
  try {
    const sale = await salesRepo.findById(req.params.id);
    if (!sale) return errorResponse(res, 'Sale not found', 404);
    return successResponse(res, sale);
  } catch (err) { next(err); }
}

async function create(req, res, next) {
  try {
    const { payment_method, amount_paid, notes, items } = req.body;
    if (!payment_method || !['tunai', 'transfer', 'qris'].includes(payment_method))
      return errorResponse(res, 'Valid payment_method required (tunai/transfer/qris)', 400);
    if (!items || !items.length) return errorResponse(res, 'Cart is empty', 400);

    // Pre-validate all items before opening transaction
    const salesItems = [];
    let total = 0;

    for (const item of items) {
      if (item.item_type === 'product') {
        const product = await productRepo.findById(item.product_id);
        if (!product) return errorResponse(res, `Product ${item.product_id} not found`, 404);
        if (parseFloat(product.stock) < parseFloat(item.qty))
          return errorResponse(res, `Stok ${product.name} tidak cukup (sisa: ${product.stock})`, 400);
        const subtotal = parseFloat(product.selling_price) * parseFloat(item.qty);
        total += subtotal;
        salesItems.push({
          item_type: 'product', product_id: product.id, package_id: null,
          item_name: product.name, qty: parseFloat(item.qty),
          unit_price: parseFloat(product.selling_price), subtotal,
          _stock_deductions: [{ product_id: product.id, qty: parseFloat(item.qty) }],
        });
      } else if (item.item_type === 'package') {
        const pkg = await packageRepo.findById(item.package_id);
        if (!pkg) return errorResponse(res, `Package ${item.package_id} not found`, 404);
        const pkgItems = (pkg.items || []).filter(i => i && i.product_id);
        const deductions = [];
        for (const pi of pkgItems) {
          const needed = parseFloat(pi.qty) * parseFloat(item.qty);
          const prod = await productRepo.findById(pi.product_id);
          if (!prod || parseFloat(prod.stock) < needed)
            return errorResponse(res, `Stok ${pi.product_name} tidak cukup untuk ${pkg.name}`, 400);
          deductions.push({ product_id: pi.product_id, qty: needed });
        }
        const subtotal = parseFloat(pkg.selling_price) * parseFloat(item.qty);
        total += subtotal;
        salesItems.push({
          item_type: 'package', product_id: null, package_id: pkg.id,
          item_name: pkg.name, qty: parseFloat(item.qty),
          unit_price: parseFloat(pkg.selling_price), subtotal,
          _stock_deductions: deductions,
        });
      } else {
        return errorResponse(res, `Invalid item_type: ${item.item_type}`, 400);
      }
    }

    if (payment_method === 'tunai' && parseFloat(amount_paid || 0) < total)
      return errorResponse(res, 'Uang bayar kurang dari total', 400);

    const change = payment_method === 'tunai' ? parseFloat(amount_paid) - total : 0;
    const invoice_no = generateInvoiceNo();

    // Single transaction for sale + stock movements
    const client = await getClient();
    let saleId;
    try {
      await client.query('BEGIN');

      const saleRes = await client.query(
        `INSERT INTO sales (invoice_no,sales_date,payment_method,total_amount,amount_paid,change_amount,notes,cashier_id)
         VALUES ($1,CURRENT_DATE,$2,$3,$4,$5,$6,$7) RETURNING *`,
        [invoice_no, payment_method, total, parseFloat(amount_paid) || total, change, notes || null, req.user.id]
      );
      const sale = saleRes.rows[0];
      saleId = sale.id;

      for (const item of salesItems) {
        await client.query(
          `INSERT INTO sales_items (sales_id,item_type,product_id,package_id,item_name,qty,unit_price,subtotal)
           VALUES ($1,$2,$3,$4,$5,$6,$7,$8)`,
          [sale.id, item.item_type, item.product_id, item.package_id, item.item_name, item.qty, item.unit_price, item.subtotal]
        );
        for (const ded of item._stock_deductions) {
          const stockRes = await client.query('SELECT stock FROM products WHERE id=$1 FOR UPDATE', [ded.product_id]);
          const qtyBefore = parseFloat(stockRes.rows[0].stock);
          const qtyAfter = qtyBefore - ded.qty;
          await client.query('UPDATE products SET stock=$2 WHERE id=$1', [ded.product_id, qtyAfter]);
          await client.query(
            `INSERT INTO stock_movements (product_id,movement_type,qty,qty_before,qty_after,reference_id,reference_type,created_by)
             VALUES ($1,'sale',$2,$3,$4,$5,'sale',$6)`,
            [ded.product_id, ded.qty, qtyBefore, qtyAfter, sale.id, req.user.id]
          );
        }
      }

      await client.query('COMMIT');
    } catch (err) {
      await client.query('ROLLBACK');
      throw err;
    } finally {
      client.release();
    }

    const fullSale = await salesRepo.findById(saleId);
    await logAudit({ userId: req.user.id, action: 'create_sale', entity: 'sales', entityId: saleId, newData: { invoice_no, total }, req });
    return successResponse(res, fullSale, 'Transaksi berhasil', 201);
  } catch (err) { next(err); }
}

async function voidSale(req, res, next) {
  try {
    const { id } = req.params;
    const sale = await salesRepo.findById(id);
    if (!sale) return errorResponse(res, 'Sale not found', 404);
    if (sale.status === 'void') return errorResponse(res, 'Sale already voided', 400);
    const updated = await salesRepo.voidSale(id);
    await logAudit({ userId: req.user.id, action: 'void_sale', entity: 'sales', entityId: id, oldData: sale, req });
    return successResponse(res, updated, 'Sale voided');
  } catch (err) { next(err); }
}

module.exports = { getAll, getById, create, voidSale };
