const { CashDrawerSession, CashDrawerActivity, Register, User, Store, sequelize } = require('../models');
const { Op } = require('sequelize');

/**
 * Open a new cash drawer session
 */
exports.openSession = async (req, res) => {
  const transaction = await sequelize.transaction();
  
  try {
    const { registerId, openingCash, notes } = req.body;
    const userId = req.user.id; // From authentication middleware

    // Validate required fields
    if (!registerId || openingCash === undefined) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'Register ID and opening cash amount are required'
      });
    }

    // Check if register exists
    const register = await Register.findByPk(registerId);
    if (!register) {
      await transaction.rollback();
      return res.status(404).json({
        success: false,
        message: 'Register not found'
      });
    }

    // Check for existing open session on this register
    const existingSession = await CashDrawerSession.findOne({
      where: {
        registerId,
        closedAt: null
      }
    });

    if (existingSession) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'A session is already open for this register'
      });
    }

    // Create new session
    const session = await CashDrawerSession.create({
      registerId,
      openedBy: userId,
      openedAt: new Date(),
      openingCash: parseFloat(openingCash),
      expectedCash: parseFloat(openingCash),
      totalSales: 0,
      totalRefunds: 0,
      cashIn: 0,
      cashOut: 0,
      notes
    }, { transaction });

    // Log the activity
    await CashDrawerActivity.create({
      registerId,
      sessionId: session.id,
      storeId: register.storeId,
      activityType: 'OPEN',
      amount: parseFloat(openingCash),
      balanceBefore: 0,
      balanceAfter: parseFloat(openingCash),
      reason: 'Session opened',
      performedBy: userId,
      performedAt: new Date()
    }, { transaction });

    await transaction.commit();

    // Fetch complete session data
    const fullSession = await CashDrawerSession.findByPk(session.id, {
      include: [
        { model: Register, as: 'register' },
        { model: User, as: 'openedUser', attributes: ['id', 'firstName', 'email'] }
      ]
    });

    res.status(201).json({
      success: true,
      message: 'Cash drawer session opened successfully',
      data: fullSession
    });
  } catch (error) {
    await transaction.rollback();
    console.error('Error opening cash drawer session:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to open cash drawer session',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Close an existing cash drawer session
 */
exports.closeSession = async (req, res) => {
  const transaction = await sequelize.transaction();
  
  try {
    const { sessionId } = req.params;
    const { closingCash, notes } = req.body;
    const userId = req.user.id;

    if (closingCash === undefined) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'Closing cash amount is required'
      });
    }

    // Find the session
    const session = await CashDrawerSession.findByPk(sessionId, {
      include: [{ model: Register, as: 'register' }]
    });

    if (!session) {
      await transaction.rollback();
      return res.status(404).json({
        success: false,
        message: 'Session not found'
      });
    }

    if (session.closedAt) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'Session is already closed'
      });
    }

    // Calculate variance
    const closingAmount = parseFloat(closingCash);
    const variance = closingAmount - parseFloat(session.expectedCash);

    // Update session
    await session.update({
      closedBy: userId,
      closedAt: new Date(),
      closingCash: closingAmount,
      variance,
      notes: notes || session.notes
    }, { transaction });

    // Log the activity
    await CashDrawerActivity.create({
      registerId: session.registerId,
      sessionId: session.id,
      storeId: session.register.storeId,
      activityType: 'CLOSE',
      amount: closingAmount,
      balanceBefore: parseFloat(session.expectedCash),
      balanceAfter: 0,
      reason: `Session closed. Variance: ${variance}`,
      performedBy: userId,
      performedAt: new Date()
    }, { transaction });

    await transaction.commit();

    // Fetch complete session data
    const fullSession = await CashDrawerSession.findByPk(sessionId, {
      include: [
        { model: Register, as: 'register' },
        { model: User, as: 'openedUser', attributes: ['id', 'firstName', 'email'] },
        { model: User, as: 'closedUser', attributes: ['id', 'firstName', 'email'] }
      ]
    });

    res.json({
      success: true,
      message: 'Cash drawer session closed successfully',
      data: fullSession
    });
  } catch (error) {
    await transaction.rollback();
    console.error('Error closing cash drawer session:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to close cash drawer session',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Record cash in/out activity
 */
exports.recordActivity = async (req, res) => {
  const transaction = await sequelize.transaction();
  
  try {
    const { registerId, activityType, amount, reason } = req.body;
    const userId = req.user.id;

    // Validate required fields
    if (!registerId || !activityType || amount === undefined) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'Register ID, activity type, and amount are required'
      });
    }

    // Validate activity type
    const validTypes = ['CASH_IN', 'CASH_OUT', 'SALE', 'REFUND', 'ADJUSTMENT'];
    if (!validTypes.includes(activityType)) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'Invalid activity type'
      });
    }

    // Find active session for this register
    const session = await CashDrawerSession.findOne({
      where: {
        registerId,
        closedAt: null
      },
      include: [{ model: Register, as: 'register' }]
    });

    if (!session) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: 'No active session found for this register'
      });
    }

    const activityAmount = parseFloat(amount);
    const balanceBefore = parseFloat(session.expectedCash);
    
    // Calculate balance change based on activity type
    let balanceChange = 0;
    switch (activityType) {
      case 'CASH_IN':
      case 'SALE':
        balanceChange = activityAmount;
        break;
      case 'CASH_OUT':
      case 'REFUND':
        balanceChange = -activityAmount;
        break;
      case 'ADJUSTMENT':
        balanceChange = activityAmount; // Can be positive or negative
        break;
    }

    const balanceAfter = balanceBefore + balanceChange;

    // Create activity record
    const activity = await CashDrawerActivity.create({
      registerId,
      sessionId: session.id,
      storeId: session.register.storeId,
      activityType,
      amount: activityAmount,
      balanceBefore,
      balanceAfter,
      reason: reason || `${activityType} transaction`,
      performedBy: userId,
      performedAt: new Date()
    }, { transaction });

    // Update session totals
    const updateData = { expectedCash: balanceAfter };
    
    switch (activityType) {
      case 'SALE':
        updateData.totalSales = parseFloat(session.totalSales) + activityAmount;
        break;
      case 'REFUND':
        updateData.totalRefunds = parseFloat(session.totalRefunds) + activityAmount;
        break;
      case 'CASH_IN':
        updateData.cashIn = parseFloat(session.cashIn) + activityAmount;
        break;
      case 'CASH_OUT':
        updateData.cashOut = parseFloat(session.cashOut) + activityAmount;
        break;
    }

    await session.update(updateData, { transaction });

    await transaction.commit();

    // Fetch complete activity data
    const fullActivity = await CashDrawerActivity.findByPk(activity.id, {
      include: [
        { model: User, as: 'user', attributes: ['id', 'firstName', 'email'] },
        { model: Register, as: 'register' }
      ]
    });

    res.status(201).json({
      success: true,
      message: 'Activity recorded successfully',
      data: fullActivity
    });
  } catch (error) {
    await transaction.rollback();
    console.error('Error recording cash drawer activity:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to record activity',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Get current session for a register
 */
exports.getCurrentSession = async (req, res) => {
  try {
    const { registerId } = req.params;

    const session = await CashDrawerSession.findOne({
      where: {
        registerId,
        closedAt: null
      },
      include: [
        { model: Register, as: 'register' },
        { model: User, as: 'openedUser', attributes: ['id', 'firstName', 'email'] }
      ]
    });

    if (!session) {
      return res.status(404).json({
        success: false,
        message: 'No active session found for this register'
      });
    }

    res.json({
      success: true,
      data: session
    });
  } catch (error) {
    console.error('Error fetching current session:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to fetch current session',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Get session history with filters
 */
exports.getSessionHistory = async (req, res) => {
  try {
    const { registerId, startDate, endDate, page = 1, limit = 20 } = req.query;

    const where = {};
    
    if (registerId) {
      where.registerId = registerId;
    }

    if (startDate || endDate) {
      where.openedAt = {};
      if (startDate) where.openedAt[Op.gte] = new Date(startDate);
      if (endDate) where.openedAt[Op.lte] = new Date(endDate);
    }

    const offset = (parseInt(page) - 1) * parseInt(limit);

    const { count, rows } = await CashDrawerSession.findAndCountAll({
      where,
      include: [
        { model: Register, as: 'register' },
        { model: User, as: 'openedUser', attributes: ['id', 'firstName', 'email'] },
        { model: User, as: 'closedUser', attributes: ['id', 'firstName', 'email'] }
      ],
      order: [['openedAt', 'DESC']],
      limit: parseInt(limit),
      offset
    });

    res.json({
      success: true,
      data: {
        sessions: rows,
        pagination: {
          total: count,
          page: parseInt(page),
          limit: parseInt(limit),
          totalPages: Math.ceil(count / parseInt(limit))
        }
      }
    });
  } catch (error) {
    console.error('Error fetching session history:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to fetch session history',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Get activities for a specific session
 */
exports.getSessionActivities = async (req, res) => {
  try {
    const { sessionId } = req.params;
    const { page = 1, limit = 50 } = req.query;

    const offset = (parseInt(page) - 1) * parseInt(limit);

    const { count, rows } = await CashDrawerActivity.findAndCountAll({
      where: { sessionId },
      include: [
        { model: User, as: 'user', attributes: ['id', 'firstName', 'email'] },
        { model: Register, as: 'register' }
      ],
      order: [['performedAt', 'DESC']],
      limit: parseInt(limit),
      offset
    });

    res.json({
      success: true,
      data: {
        activities: rows,
        pagination: {
          total: count,
          page: parseInt(page),
          limit: parseInt(limit),
          totalPages: Math.ceil(count / parseInt(limit))
        }
      }
    });
  } catch (error) {
    console.error('Error fetching session activities:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to fetch activities',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Get cash drawer summary/reports
 */
exports.getSummary = async (req, res) => {
  try {
    const { registerId, startDate, endDate } = req.query;

    const where = {};
    
    if (registerId) {
      where.registerId = registerId;
    }

    if (startDate || endDate) {
      where.openedAt = {};
      if (startDate) where.openedAt[Op.gte] = new Date(startDate);
      if (endDate) where.openedAt[Op.lte] = new Date(endDate);
    }

    // Only include closed sessions for accurate reporting
    where.closedAt = { [Op.ne]: null };

    const sessions = await CashDrawerSession.findAll({
      where,
      attributes: [
        [sequelize.fn('COUNT', sequelize.col('id')), 'totalSessions'],
        [sequelize.fn('SUM', sequelize.col('totalSales')), 'totalSales'],
        [sequelize.fn('SUM', sequelize.col('totalRefunds')), 'totalRefunds'],
        [sequelize.fn('SUM', sequelize.col('cashIn')), 'totalCashIn'],
        [sequelize.fn('SUM', sequelize.col('cashOut')), 'totalCashOut'],
        [sequelize.fn('SUM', sequelize.col('variance')), 'totalVariance'],
        [sequelize.fn('AVG', sequelize.col('variance')), 'avgVariance']
      ],
      raw: true
    });

    res.json({
      success: true,
      data: sessions[0]
    });
  } catch (error) {
    console.error('Error fetching cash drawer summary:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to fetch summary',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};

/**
 * Get a specific session by ID
 */
exports.getSessionById = async (req, res) => {
  try {
    const { sessionId } = req.params;

    const session = await CashDrawerSession.findByPk(sessionId, {
      include: [
        { model: Register, as: 'register' },
        { model: User, as: 'openedUser', attributes: ['id', 'firstName', 'email'] },
        { model: User, as: 'closedUser', attributes: ['id', 'firstName', 'email'] }
      ]
    });

    if (!session) {
      return res.status(404).json({
        success: false,
        message: 'Session not found'
      });
    }

    res.json({
      success: true,
      data: session
    });
  } catch (error) {
    console.error('Error fetching session:', error);
    res.status(500).json({
      success: false,
      message: 'Failed to fetch session',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
};