const Transaction = require('../models/Transaction.model');
const Account = require('../models/Account.model');
const Budget = require('../models/Budget.model');

// POST /api/transactions
const createTransaction = async (req, res, next) => {
  const session = await Transaction.startSession();
  session.startTransaction();
  try {
    const { accountId, type, amount, category, description, note, date, tags, isRecurring, recurringFrequency } = req.body;

    const account = await Account.findOne({ _id: accountId, user: req.user._id }).session(session);
    if (!account) {
      await session.abortTransaction();
      return res.status(404).json({ success: false, message: 'Account not found.' });
    }

    // Check balance for expense
    if (type === 'expense' && account.availableBalance < amount) {
      await session.abortTransaction();
      return res.status(400).json({ success: false, message: 'Insufficient available balance.' });
    }

    const transaction = await Transaction.create([{
      user: req.user._id,
      account: accountId,
      type,
      amount,
      category,
      description,
      note,
      date: date ? new Date(date) : new Date(),
      tags,
      isRecurring,
      recurringFrequency,
    }], { session });

    // Update account balance
    if (type === 'income') {
      account.balance += amount;
      account.availableBalance += amount;
    } else if (type === 'expense') {
      account.balance -= amount;
      account.availableBalance -= amount;
    }
    await account.save({ session });

    // Update linked budget if applicable
    if (type === 'expense') {
      const now = new Date();
      const budget = await Budget.findOne({
        user: req.user._id,
        category,
        isActive: true,
        startDate: { $lte: now },
        endDate: { $gte: now },
      }).session(session);

      if (budget) {
        budget.spentAmount += amount;

        // Persist budgetId on the transaction
        await Transaction.findByIdAndUpdate(
          transaction[0]._id,
          { budgetId: budget._id },
          { session }
        );

        // Check alert thresholds
        const pct = (budget.spentAmount / budget.limitAmount) * 100;
        if (pct >= 100 && !budget.alert100Sent) {
          budget.alert100Sent = true;
          // TODO: push notification
        } else if (pct >= 80 && !budget.alert80Sent) {
          budget.alert80Sent = true;
          // TODO: push notification
        }
        await budget.save({ session });
      }
    }

    await session.commitTransaction();

    res.status(201).json({
      success: true,
      message: 'Transaction added successfully.',
      data: {
        transaction: transaction[0],
        account: { balance: account.balance, availableBalance: account.availableBalance },
      },
    });
  } catch (error) {
    await session.abortTransaction();
    next(error);
  } finally {
    session.endSession();
  }
};

// GET /api/transactions
const getTransactions = async (req, res, next) => {
  try {
    const {
      page = 1,
      limit = 20,
      type,
      category,
      startDate,
      endDate,
      accountId,
      search,
      sortBy = 'date',
      sortOrder = 'desc',
    } = req.query;

    const filter = { user: req.user._id };
    if (type) filter.type = type;
    if (category) filter.category = category;
    if (accountId) filter.account = accountId;
    if (search) {
      filter.$or = [
        { description: { $regex: search, $options: 'i' } },
        { note: { $regex: search, $options: 'i' } },
        { referenceNumber: { $regex: search, $options: 'i' } },
      ];
    }
    if (startDate || endDate) {
      filter.date = {};
      if (startDate) filter.date.$gte = new Date(startDate);
      if (endDate) filter.date.$lte = new Date(endDate);
    }

    const skip = (parseInt(page) - 1) * parseInt(limit);
    const sort = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };

    const [transactions, total] = await Promise.all([
      Transaction.find(filter)
        .populate('account', 'name color icon currency')
        .sort(sort)
        .skip(skip)
        .limit(parseInt(limit)),
      Transaction.countDocuments(filter),
    ]);

    res.json({
      success: true,
      data: {
        transactions,
        pagination: {
          page: parseInt(page),
          limit: parseInt(limit),
          total,
          pages: Math.ceil(total / parseInt(limit)),
        },
      },
    });
  } catch (error) {
    next(error);
  }
};

// GET /api/transactions/:id
const getTransaction = async (req, res, next) => {
  try {
    const transaction = await Transaction.findOne({ _id: req.params.id, user: req.user._id })
      .populate('account', 'name color icon currency');
    if (!transaction) {
      return res.status(404).json({ success: false, message: 'Transaction not found.' });
    }
    res.json({ success: true, data: { transaction } });
  } catch (error) {
    next(error);
  }
};

// DELETE /api/transactions/:id
const deleteTransaction = async (req, res, next) => {
  const session = await Transaction.startSession();
  session.startTransaction();
  try {
    const transaction = await Transaction.findOne({ _id: req.params.id, user: req.user._id }).session(session);
    if (!transaction) {
      await session.abortTransaction();
      return res.status(404).json({ success: false, message: 'Transaction not found.' });
    }

    const account = await Account.findById(transaction.account).session(session);
    if (account) {
      // Reverse balance effect
      if (transaction.type === 'income') {
        account.balance -= transaction.amount;
        account.availableBalance -= transaction.amount;
      } else if (transaction.type === 'expense') {
        account.balance += transaction.amount;
        account.availableBalance += transaction.amount;
      }
      await account.save({ session });
    }

    // Reverse budget spending
    if (transaction.type === 'expense' && transaction.budgetId) {
      await Budget.findByIdAndUpdate(transaction.budgetId, {
        $inc: { spentAmount: -transaction.amount },
      }).session(session);
    }

    await Transaction.deleteOne({ _id: transaction._id }).session(session);
    await session.commitTransaction();
    res.json({ success: true, message: 'Transaction deleted.' });
  } catch (error) {
    await session.abortTransaction();
    next(error);
  } finally {
    session.endSession();
  }
};

// GET /api/transactions/summary
const getTransactionSummary = async (req, res, next) => {
  try {
    const { period = 'month', year = new Date().getFullYear(), month = new Date().getMonth() + 1 } = req.query;

    let startDate, endDate;
    if (period === 'month') {
      startDate = new Date(year, month - 1, 1);
      endDate = new Date(year, month, 0, 23, 59, 59);
    } else if (period === 'year') {
      startDate = new Date(year, 0, 1);
      endDate = new Date(year, 11, 31, 23, 59, 59);
    }

    const summary = await Transaction.aggregate([
      {
        $match: {
          user: req.user._id,
          date: { $gte: startDate, $lte: endDate },
          status: 'completed',
        },
      },
      {
        $group: {
          _id: '$type',
          total: { $sum: '$amount' },
          count: { $sum: 1 },
        },
      },
    ]);

    const categoryBreakdown = await Transaction.aggregate([
      {
        $match: {
          user: req.user._id,
          type: 'expense',
          date: { $gte: startDate, $lte: endDate },
        },
      },
      {
        $group: {
          _id: '$category',
          total: { $sum: '$amount' },
          count: { $sum: 1 },
        },
      },
      { $sort: { total: -1 } },
      { $limit: 10 },
    ]);

    res.json({
      success: true,
      data: { summary, categoryBreakdown, period, startDate, endDate },
    });
  } catch (error) {
    next(error);
  }
};

module.exports = { createTransaction, getTransactions, getTransaction, deleteTransaction, getTransactionSummary };
