import { Request, Response } from 'express';
import { logActivity } from '../../../helpers/activityLog.helper';
import { buildTextSearch, parsePagination, paginationMeta } from '../../../helpers/query.helper';
import { Product, StockMovement } from '../schema/product.schema';

async function attachSalesStats(products: Array<{ _id: { toString: () => string }; toObject: () => Record<string, unknown> }>) {
  const ids = products.map((p) => p._id);
  const salesAgg = await StockMovement.aggregate([
    { $match: { type: 'OUT', productId: { $in: ids } } },
        {
          $lookup: {
            from: 'products',
            localField: 'productId',
            foreignField: '_id',
            as: 'product',
          },
        },
        { $unwind: { path: '$product', preserveNullAndEmptyArrays: true } },
        {
          $group: {
            _id: '$productId',
            unitsSold: { $sum: '$quantity' },
            salesRevenue: {
              $sum: {
                $multiply: [
                  '$quantity',
                  {
                    $ifNull: [
                      '$product.sellingPrice',
                      { $ifNull: ['$product.price', 0] },
                    ],
                  },
                ],
              },
            },
          },
        },
      ]);

  type SalesSummary = { unitsSold: number; salesRevenue: number };
  const defaultSales: SalesSummary = { unitsSold: 0, salesRevenue: 0 };
  const salesMap = new Map<string, SalesSummary>(
    salesAgg.map((row: { _id: unknown; unitsSold: number; salesRevenue: number }) => [
      String(row._id),
      { unitsSold: row.unitsSold, salesRevenue: row.salesRevenue },
    ])
  );

  return products.map((product) => {
    const sales = salesMap.get(String(product._id)) ?? defaultSales;
    const plain = product.toObject() as Record<string, unknown>;
    return {
      ...plain,
      unitsSold: sales.unitsSold,
      salesRevenue: sales.salesRevenue,
    };
  });
}

export class ProductController {
  static async list(req: Request, res: Response) {
    try {
      const { page, limit, skip } = parsePagination(req, 10);
      const filter: Record<string, unknown> = {};
      const search = buildTextSearch(['name', 'sku', 'unit'], String(req.query.q || ''));
      if (search.$or) Object.assign(filter, search);

      const [products, total] = await Promise.all([
        Product.find(filter).sort({ name: 1 }).skip(skip).limit(limit),
        Product.countDocuments(filter),
      ]);

      const [response, salesTotals] = await Promise.all([
        attachSalesStats(products),
        StockMovement.aggregate([
          { $match: { type: 'OUT' } },
          {
            $lookup: {
              from: 'products',
              localField: 'productId',
              foreignField: '_id',
              as: 'product',
            },
          },
          { $unwind: { path: '$product', preserveNullAndEmptyArrays: true } },
          {
            $group: {
              _id: null,
              unitsSold: { $sum: '$quantity' },
              revenue: {
                $sum: {
                  $multiply: [
                    '$quantity',
                    {
                      $ifNull: [
                        '$product.sellingPrice',
                        { $ifNull: ['$product.price', 0] },
                      ],
                    },
                  ],
                },
              },
            },
          },
        ]),
      ]);

      const totals = salesTotals[0] || { unitsSold: 0, revenue: 0 };

      return res.status(200).json({
        success: true,
        response,
        summary: {
          unitsSold: totals.unitsSold || 0,
          revenue: totals.revenue || 0,
        },
        pagination: paginationMeta(page, limit, total),
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async create(req: Request, res: Response) {
    try {
      const response = await Product.create(req.body);
      await logActivity(req, 'CREATE', 'PRODUCT', response._id.toString(), response.name);
      return res.status(201).json({ success: true, response });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async update(req: Request, res: Response) {
    try {
      const response = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true });
      if (!response) return res.status(404).json({ success: false, message: 'Not found' });
      await logActivity(req, 'UPDATE', 'PRODUCT', response._id.toString(), response.name);
      return res.status(200).json({ success: true, response });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async remove(req: Request, res: Response) {
    try {
      const product = await Product.findById(req.params.id);
      if (!product) return res.status(404).json({ success: false, message: 'Not found' });
      await Product.findByIdAndDelete(req.params.id);
      await logActivity(req, 'DELETE', 'PRODUCT', req.params.id, product.name);
      return res.status(200).json({ success: true, message: 'Deleted' });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async listMovements(req: Request, res: Response) {
    try {
      const { page, limit, skip } = parsePagination(req, 10);
      const filter: Record<string, unknown> = {};

      const type = String(req.query.type || '').trim();
      if (type) filter.type = type;

      const q = String(req.query.q || '').trim();
      if (q) {
        const products = await Product.find(buildTextSearch(['name', 'sku'], q));
        filter.productId = { $in: products.map((p) => p._id) };
      }

      const [items, total] = await Promise.all([
        StockMovement.find(filter).sort({ createdAt: -1 }).skip(skip).limit(limit).populate('productId'),
        StockMovement.countDocuments(filter),
      ]);

      return res.status(200).json({
        success: true,
        response: items,
        pagination: paginationMeta(page, limit, total),
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async recordMovement(req: Request, res: Response) {
    try {
      const { productId, type, quantity, reason, appointmentId } = req.body;
      if (!productId || !type || !quantity) {
        return res.status(400).json({ success: false, message: 'productId, type and quantity required' });
      }

      const product = await Product.findById(productId);
      if (!product) return res.status(404).json({ success: false, message: 'Product not found' });

      const delta = type === 'IN' ? quantity : -quantity;
      const nextQty = product.quantity + delta;
      if (nextQty < 0) {
        return res.status(400).json({ success: false, message: 'Insufficient stock' });
      }

      product.quantity = nextQty;
      await product.save();

      const movement = await StockMovement.create({
        productId,
        type,
        quantity,
        reason,
        appointmentId,
        performedBy: req['staffUser']?._id?.toString() || null,
      });

      await logActivity(req, 'STOCK_' + type, 'PRODUCT', productId, `${quantity} ${product.name}`);

      return res.status(201).json({ success: true, response: { movement, product } });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }
}
