import dotenv from 'dotenv';
import { stockAuditModel } from '../../database/schemas/inventory/stockaudit.schema.js';
import log4js from 'log4js';
import mongoose from 'mongoose';
import { getAuditLogs } from '../../utils.js';
import { getModelStats, getModelHistory } from '../../database/database.js';

dotenv.config();

const logger = log4js.getLogger('Stock Audits');
logger.level = process.env.LOG_LEVEL;

export const listStockAuditsRouteHandler = async (
  req,
  res,
  page = 1,
  limit = 25,
  property = '',
  filter = {}
) => {
  try {
    const skip = (page - 1) * limit;
    let stockAudits;
    let aggregateCommand = [];

    // Lookup createdBy user
    aggregateCommand.push({
      $lookup: {
        from: 'users',
        localField: 'createdBy',
        foreignField: '_id',
        as: 'createdBy',
      },
    });

    aggregateCommand.push({ $unwind: '$createdBy' });

    if (filter != {}) {
      aggregateCommand.push({ $match: filter });
    }

    if (property != '') {
      aggregateCommand.push({ $group: { _id: `$${property}` } });
      aggregateCommand.push({ $project: { _id: 0, [property]: '$_id' } });
    }

    aggregateCommand.push({ $skip: skip });
    aggregateCommand.push({ $limit: Number(limit) });

    stockAudits = await stockAuditModel.aggregate(aggregateCommand);

    logger.trace(
      `List of stock audits (Page ${page}, Limit ${limit}, Property ${property}):`,
      stockAudits
    );
    res.send(stockAudits);
  } catch (error) {
    logger.error('Error listing stock audits:', error);
    res.status(500).send({ error: error });
  }
};

export const getStockAuditRouteHandler = async (req, res) => {
  try {
    const id = new mongoose.Types.ObjectId(req.params.id);
    const stockAudit = await stockAuditModel
      .findOne({
        _id: id,
      })
      .populate('createdBy')
      .populate('items.filamentStock')
      .populate('items.partStock');

    if (!stockAudit) {
      logger.warn(`Stock audit not found with supplied id.`);
      return res.status(404).send({ error: 'Stock audit not found.' });
    }

    logger.trace(`Stock audit with ID: ${id}:`, stockAudit);

    const auditLogs = await getAuditLogs(id);

    res.send({ ...stockAudit._doc, auditLogs: auditLogs });
  } catch (error) {
    logger.error('Error fetching stock audit:', error);
    res.status(500).send({ error: error.message });
  }
};

export const newStockAuditRouteHandler = async (req, res) => {
  try {
    const newStockAudit = {
      type: req.body.type,
      status: req.body.status || 'pending',
      notes: req.body.notes,
      items: req.body.items.map((item) => ({
        type: item.type,
        stock:
          item.type === 'filament'
            ? new mongoose.Types.ObjectId(item.filamentStock)
            : new mongoose.Types.ObjectId(item.partStock),
        expectedQuantity: item.expectedQuantity,
        actualQuantity: item.actualQuantity,
        notes: item.notes,
      })),
      createdBy: new mongoose.Types.ObjectId(req.body.createdBy),
      completedAt: req.body.status === 'completed' ? new Date() : null,
    };

    const result = await stockAuditModel.create(newStockAudit);
    if (!result) {
      logger.error('No stock audit created.');
      return res.status(500).send({ error: 'No stock audit created.' });
    }
    return res.send({ status: 'ok', id: result._id });
  } catch (error) {
    logger.error('Error adding stock audit:', error);
    return res.status(500).send({ error: error.message });
  }
};

export const updateStockAuditRouteHandler = async (req, res) => {
  try {
    const id = new mongoose.Types.ObjectId(req.params.id);
    const updateData = {
      ...req.body,
      items: req.body.items?.map((item) => ({
        type: item.type,
        stock:
          item.type === 'filament'
            ? new mongoose.Types.ObjectId(item.filamentStock)
            : new mongoose.Types.ObjectId(item.partStock),
        expectedQuantity: item.expectedQuantity,
        actualQuantity: item.actualQuantity,
        notes: item.notes,
      })),
      completedAt: req.body.status === 'completed' ? new Date() : null,
    };

    const result = await stockAuditModel.findByIdAndUpdate(id, { $set: updateData }, { new: true });

    if (!result) {
      logger.warn(`Stock audit not found with supplied id.`);
      return res.status(404).send({ error: 'Stock audit not found.' });
    }

    logger.trace(`Updated stock audit with ID: ${id}:`, result);
    res.send(result);
  } catch (error) {
    logger.error('Error updating stock audit:', error);
    res.status(500).send({ error: error.message });
  }
};

export const deleteStockAuditRouteHandler = async (req, res) => {
  try {
    const id = new mongoose.Types.ObjectId(req.params.id);
    const result = await stockAuditModel.findByIdAndDelete(id);

    if (!result) {
      logger.warn(`Stock audit not found with supplied id.`);
      return res.status(404).send({ error: 'Stock audit not found.' });
    }

    logger.trace(`Deleted stock audit with ID: ${id}`);
    res.send({ status: 'ok' });
  } catch (error) {
    logger.error('Error deleting stock audit:', error);
    res.status(500).send({ error: error.message });
  }
};

export const getStockAuditStatsRouteHandler = async (req, res) => {
  const result = await getModelStats({ model: stockAuditModel });
  if (result?.error) {
    logger.error('Error fetching stock audit stats:', result.error);
    return res.status(result.code).send(result);
  }
  logger.trace('Stock audit stats:', result);
  res.send(result);
};

export const getStockAuditHistoryRouteHandler = async (req, res) => {
  const from = req.query.from;
  const to = req.query.to;
  const result = await getModelHistory({ model: stockAuditModel, from, to });
  if (result?.error) {
    logger.error('Error fetching stock audit history:', result.error);
    return res.status(result.code).send(result);
  }
  logger.trace('Stock audit history:', result);
  res.send(result);
};
