// controllers/commandesController.js
const Commande = require('../models/Commande');
const CommandeItem = require('../models/CommandeItem');
const Menu = require('../models/Menu');
const { Op } = require('sequelize');
const sequelize = require('../config/database');

// Helper function to validate and convert numbers
const validateNumber = (value, defaultValue = 0) => {
  const num = parseFloat(value);
  return isNaN(num) ? defaultValue : num;
};

// Helper function to validate integer
const validateInteger = (value, defaultValue = 1) => {
  const num = parseInt(value);
  return isNaN(num) ? defaultValue : num;
};

// Get all commandes with menu details
const getAllCommandes = async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const offset = (page - 1) * limit;
    
    const where = {};
    
    // Filters
    if (req.query.statut) {
      where.statut = req.query.statut;
    }
    
    if (req.query.client_id) {
      where.client_id = req.query.client_id;
    }
    
    if (req.query.table_id) {
      where.table_id = req.query.table_id;
    }
    
    if (req.query.serveur) {
      where.serveur = { [Op.like]: `%${req.query.serveur}%` };
    }
    
    if (req.query.date_debut && req.query.date_fin) {
      where.date_commande = {
        [Op.between]: [req.query.date_debut, req.query.date_fin]
      };
    }

    const { count, rows } = await Commande.findAndCountAll({
      where,
      limit,
      offset,
      order: [['date_commande', 'DESC']]
    });

    // Fetch items with menu details for each commande
    const commandeIds = rows.map(c => c.id);
    let itemsWithMenus = [];
    
    if (commandeIds.length > 0) {
      itemsWithMenus = await sequelize.query(`
        SELECT 
          ci.*,
          m.nom as menu_nom,
          m.commentaire as menu_description,
          m.prix as menu_prix_actuel
        FROM commande_items ci
        LEFT JOIN menus m ON ci.menu_id = m.id
        WHERE ci.commande_id IN (${commandeIds.join(',')})
        ORDER BY ci.id
      `, {
        type: sequelize.QueryTypes.SELECT
      });
    }

    // Group items by commande_id
    const itemsByCommandeId = itemsWithMenus.reduce((acc, item) => {
      if (!acc[item.commande_id]) {
        acc[item.commande_id] = [];
      }
      acc[item.commande_id].push(item);
      return acc;
    }, {});

    // Add items to each commande
    rows.forEach(commande => {
      commande.dataValues.items = itemsByCommandeId[commande.id] || [];
    });

    res.status(200).json({
      success: true,
      data: {
        commandes: rows,
        pagination: {
          currentPage: page,
          totalPages: Math.ceil(count / limit),
          totalItems: count,
          itemsPerPage: limit
        }
      }
    });
  } catch (error) {
    console.error('Error in getAllCommandes:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la récupération des commandes',
      error: error.message
    });
  }
};

// Get commande by ID with menu details
const getCommandeById = async (req, res) => {
  try {
    const commande = await Commande.findByPk(req.params.id);

    if (!commande) {
      return res.status(404).json({
        success: false,
        message: 'Commande non trouvée'
      });
    }

    // Fetch items with menu details
    const itemsWithMenus = await sequelize.query(`
      SELECT 
        ci.*,
        m.nom as menu_nom,
        m.commentaire as menu_description,
        m.prix as menu_prix_actuel
      FROM commande_items ci
      LEFT JOIN menus m ON ci.menu_id = m.id
      WHERE ci.commande_id = :commandeId
      ORDER BY ci.id
    `, {
      replacements: { commandeId: req.params.id },
      type: sequelize.QueryTypes.SELECT
    });

    commande.dataValues.items = itemsWithMenus;

    res.status(200).json({
      success: true,
      data: commande
    });
  } catch (error) {
    console.error('Error in getCommandeById:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la récupération de la commande',
      error: error.message
    });
  }
};

// Create new commande with items (fetch prices from menu)
const createCommande = async (req, res) => {
  let transaction;
  
  try {
    // Start transaction
    transaction = await sequelize.transaction();
    
    console.log('Received data:', JSON.stringify(req.body, null, 2));

    const {
      client_id,
      table_id,
      reservation_id,
      numero_commande,
      statut,
      total_ht,
      total_tva,
      total_ttc,
      mode_paiement,
      commentaires,
      serveur,
      date_commande,
      date_service,
      items // Array of items with menu_id and quantite
    } = req.body;

    // Validate required fields
    if (!table_id) {
      return res.status(400).json({
        success: false,
        message: 'table_id est requis'
      });
    }

    // Create the commande first
    const commandeData = {
      client_id: client_id || null,
      table_id: validateInteger(table_id),
      reservation_id: reservation_id || null,
      numero_commande: numero_commande || null,
      statut: statut || 'en_attente',
      total_ht: validateNumber(total_ht, 0).toFixed(2),
      total_tva: validateNumber(total_tva, 0).toFixed(2),
      total_ttc: validateNumber(total_ttc, 0).toFixed(2),
      mode_paiement: mode_paiement || null,
      commentaires: commentaires || null,
      serveur: serveur || null,
      date_commande: date_commande || new Date(),
      date_service: date_service || null
    };

    console.log('Validated commande data:', JSON.stringify(commandeData, null, 2));

    const newCommande = await Commande.create(commandeData, { transaction });

    // If items are provided, create them with prices from menu
    if (items && Array.isArray(items) && items.length > 0) {
      console.log('Processing items:', JSON.stringify(items, null, 2));

      const commandeItems = [];
      let calculatedTotal = 0;

      // Get all menu IDs from items
      const menuIds = items.map(item => validateInteger(item.menu_id)).filter(id => id > 0);
      
      if (menuIds.length === 0) {
        return res.status(400).json({
          success: false,
          message: 'Aucun menu_id valide fourni'
        });
      }

      // Fetch menu prices
      const menus = await Menu.findAll({
        where: {
          id: { [Op.in]: menuIds }
        },
        attributes: ['id', 'nom', 'prix', 'disponible'],
        transaction
      });

      // Create a map of menu_id to menu data
      const menuMap = menus.reduce((acc, menu) => {
        acc[menu.id] = menu;
        return acc;
      }, {});

      // Process each item
      for (const item of items) {
        const menu_id = validateInteger(item.menu_id);
        const quantite = validateInteger(item.quantite, 1);

        if (menu_id === 0) {
          return res.status(400).json({
            success: false,
            message: 'menu_id invalide',
            invalidItem: item
          });
        }

        // Check if menu exists
        const menu = menuMap[menu_id];
        if (!menu) {
          return res.status(400).json({
            success: false,
            message: `Menu avec ID ${menu_id} n'existe pas`,
            invalidItem: item
          });
        }

        // Check if menu is available
        if (!menu.disponible) {
          return res.status(400).json({
            success: false,
            message: `Menu "${menu.nom}" n'est plus disponible`,
            invalidItem: item
          });
        }

        // Use price from menu table
        const prix_unitaire = validateNumber(menu.prix, 0);
        const total_item = prix_unitaire * quantite;

        if (prix_unitaire === 0) {
          return res.status(400).json({
            success: false,
            message: `Prix invalide pour le menu "${menu.nom}"`,
            invalidItem: item
          });
        }

        const validatedItem = {
          commande_id: newCommande.id,
          menu_id: menu_id,
          quantite: quantite,
          prix_unitaire: prix_unitaire.toFixed(2),
          total_item: total_item.toFixed(2),
          commentaires: item.commentaires || null,
          statut: item.statut || 'commande'
        };

        commandeItems.push(validatedItem);
        calculatedTotal += total_item;
      }

      console.log('Validated items:', JSON.stringify(commandeItems, null, 2));

      await CommandeItem.bulkCreate(commandeItems, { transaction });

      // Auto-calculate totals if not provided
      if (validateNumber(total_ttc, 0) === 0) {
        await newCommande.update({
          total_ht: calculatedTotal.toFixed(2),
          total_ttc: calculatedTotal.toFixed(2)
        }, { transaction });
      }
    }

    // Commit the transaction
    await transaction.commit();
    
    // Now fetch the created commande with items and menu details (outside of transaction)
    const createdCommande = await Commande.findByPk(newCommande.id);
    const createdItemsWithMenus = await sequelize.query(`
      SELECT 
        ci.*,
        m.nom as menu_nom,
        m.commentaire as menu_description,
        m.prix as menu_prix_actuel
      FROM commande_items ci
      LEFT JOIN menus m ON ci.menu_id = m.id
      WHERE ci.commande_id = :commandeId
      ORDER BY ci.id
    `, {
      replacements: { commandeId: newCommande.id },
      type: sequelize.QueryTypes.SELECT
    });
    
    createdCommande.dataValues.items = createdItemsWithMenus;

    res.status(201).json({
      success: true,
      message: 'Commande créée avec succès',
      data: createdCommande
    });

  } catch (error) {
    // Only rollback if transaction exists and hasn't been finished
    if (transaction && !transaction.finished) {
      try {
        await transaction.rollback();
      } catch (rollbackError) {
        console.error('Error rolling back transaction:', rollbackError);
      }
    }
    
    console.error('Error in createCommande:', error);
    res.status(400).json({
      success: false,
      message: 'Erreur lors de la création de la commande',
      error: error.message
    });
  }
};

// Update commande (also fetch prices from menu)
const updateCommande = async (req, res) => {
  let transaction;
  
  try {
    transaction = await sequelize.transaction();
    
    const commandeId = req.params.id;
    const { items, ...commandeData } = req.body;

    const commande = await Commande.findByPk(commandeId);
    
    if (!commande) {
      return res.status(404).json({
        success: false,
        message: 'Commande non trouvée'
      });
    }

    // Validate numeric fields in commandeData
    if (commandeData.total_ht !== undefined) {
      commandeData.total_ht = validateNumber(commandeData.total_ht, 0).toFixed(2);
    }
    if (commandeData.total_tva !== undefined) {
      commandeData.total_tva = validateNumber(commandeData.total_tva, 0).toFixed(2);
    }
    if (commandeData.total_ttc !== undefined) {
      commandeData.total_ttc = validateNumber(commandeData.total_ttc, 0).toFixed(2);
    }

    // Update commande
    await commande.update(commandeData, { transaction });

    // If items are provided, update them with current menu prices
    if (items && Array.isArray(items) && items.length > 0) {
      // Delete existing items
      await CommandeItem.destroy({
        where: { commande_id: commandeId },
        transaction
      });

      // Get menu prices
      const menuIds = items.map(item => validateInteger(item.menu_id)).filter(id => id > 0);
      
      const menus = await Menu.findAll({
        where: {
          id: { [Op.in]: menuIds }
        },
        attributes: ['id', 'nom', 'prix', 'est_disponible'],
        transaction
      });

      const menuMap = menus.reduce((acc, menu) => {
        acc[menu.id] = menu;
        return acc;
      }, {});

      // Create new items with validation
      const commandeItems = [];
      let calculatedTotal = 0;

      for (const item of items) {
        const menu_id = validateInteger(item.menu_id);
        const quantite = validateInteger(item.quantite, 1);

        const menu = menuMap[menu_id];
        if (!menu) {
          return res.status(400).json({
            success: false,
            message: `Menu avec ID ${menu_id} n'existe pas`
          });
        }

        const prix_unitaire = validateNumber(menu.prix, 0);
        const total_item = prix_unitaire * quantite;

        const validatedItem = {
          commande_id: commandeId,
          menu_id: menu_id,
          quantite: quantite,
          prix_unitaire: prix_unitaire.toFixed(2),
          total_item: total_item.toFixed(2),
          commentaires: item.commentaires || null,
          statut: item.statut || 'commande'
        };

        commandeItems.push(validatedItem);
        calculatedTotal += total_item;
      }

      await CommandeItem.bulkCreate(commandeItems, { transaction });

      // Recalculate totals if not provided
      if (!commandeData.total_ttc) {
        await commande.update({
          total_ht: calculatedTotal.toFixed(2),
          total_ttc: calculatedTotal.toFixed(2)
        }, { transaction });
      }
    }

    await transaction.commit();

    // Fetch updated commande with items and menu details (outside of transaction)
    const updatedCommande = await Commande.findByPk(commandeId);
    const updatedItemsWithMenus = await sequelize.query(`
      SELECT 
        ci.*,
        m.nom as menu_nom,
        m.commentaire as menu_description,
        m.prix as menu_prix_actuel
      FROM commande_items ci
      LEFT JOIN menus m ON ci.menu_id = m.id
      WHERE ci.commande_id = :commandeId
      ORDER BY ci.id
    `, {
      replacements: { commandeId },
      type: sequelize.QueryTypes.SELECT
    });
    
    updatedCommande.dataValues.items = updatedItemsWithMenus;

    res.status(200).json({
      success: true,
      message: 'Commande mise à jour avec succès',
      data: updatedCommande
    });

  } catch (error) {
    // Only rollback if transaction exists and hasn't been finished
    if (transaction && !transaction.finished) {
      try {
        await transaction.rollback();
      } catch (rollbackError) {
        console.error('Error rolling back transaction:', rollbackError);
      }
    }
    
    console.error('Error in updateCommande:', error);
    res.status(400).json({
      success: false,
      message: 'Erreur lors de la mise à jour de la commande',
      error: error.message
    });
  }
};

// Delete commande
const deleteCommande = async (req, res) => {
  let transaction;
  
  try {
    transaction = await sequelize.transaction();
    
    const commandeId = req.params.id;
    
    const commande = await Commande.findByPk(commandeId);
    
    if (!commande) {
      return res.status(404).json({
        success: false,
        message: 'Commande non trouvée'
      });
    }

    // Delete items first
    await CommandeItem.destroy({
      where: { commande_id: commandeId },
      transaction
    });

    // Delete commande
    await commande.destroy({ transaction });

    await transaction.commit();

    res.status(200).json({
      success: true,
      message: 'Commande supprimée avec succès'
    });

  } catch (error) {
    if (transaction && !transaction.finished) {
      try {
        await transaction.rollback();
      } catch (rollbackError) {
        console.error('Error rolling back transaction:', rollbackError);
      }
    }
    
    console.error('Error in deleteCommande:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la suppression de la commande',
      error: error.message
    });
  }
};

// Update commande status
const updateStatut = async (req, res) => {
  try {
    const commandeId = req.params.id;
    const { statut } = req.body;

    if (!statut) {
      return res.status(400).json({
        success: false,
        message: 'Le statut est requis'
      });
    }

    const commande = await Commande.findByPk(commandeId);
    
    if (!commande) {
      return res.status(404).json({
        success: false,
        message: 'Commande non trouvée'
      });
    }

    const updateData = { statut };
    if (statut === 'servie' && !commande.date_service) {
      updateData.date_service = new Date();
    }

    await commande.update(updateData);

    res.status(200).json({
      success: true,
      message: 'Statut de la commande mis à jour avec succès',
      data: commande
    });
  } catch (error) {
    console.error('Error in updateStatut:', error);
    res.status(400).json({
      success: false,
      message: 'Erreur lors de la mise à jour du statut',
      error: error.message
    });
  }
};

// Get commandes by status with menu details
const getCommandesByStatut = async (req, res) => {
  try {
    const { statut } = req.params;
    
    const commandes = await Commande.findAll({
      where: { statut },
      order: [['date_commande', 'DESC']]
    });

    // Fetch items with menu details for each commande
    if (commandes.length > 0) {
      const commandeIds = commandes.map(c => c.id);
      const itemsWithMenus = await sequelize.query(`
        SELECT 
          ci.*,
          m.nom as menu_nom,
          m.commentaire as menu_description,
          m.prix as menu_prix_actuel
        FROM commande_items ci
        LEFT JOIN menus m ON ci.menu_id = m.id
        WHERE ci.commande_id IN (${commandeIds.join(',')})
        ORDER BY ci.id
      `, {
        type: sequelize.QueryTypes.SELECT
      });

      const itemsByCommandeId = itemsWithMenus.reduce((acc, item) => {
        if (!acc[item.commande_id]) {
          acc[item.commande_id] = [];
        }
        acc[item.commande_id].push(item);
        return acc;
      }, {});

      commandes.forEach(commande => {
        commande.dataValues.items = itemsByCommandeId[commande.id] || [];
      });
    }

    res.status(200).json({
      success: true,
      data: commandes
    });

  } catch (error) {
    console.error('Error in getCommandesByStatut:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la récupération des commandes par statut',
      error: error.message
    });
  }
};

// Get daily statistics
const getDailyStats = async (req, res) => {
  try {
    const date = req.query.date || new Date().toISOString().split('T')[0];
    
    const startOfDay = new Date(date + 'T00:00:00.000Z');
    const endOfDay = new Date(date + 'T23:59:59.999Z');

    const stats = await Commande.findAll({
      where: {
        date_commande: {
          [Op.between]: [startOfDay, endOfDay]
        }
      },
      attributes: [
        'statut',
        [sequelize.fn('COUNT', sequelize.col('id')), 'count'],
        [sequelize.fn('SUM', sequelize.col('total_ttc')), 'total_amount']
      ],
      group: ['statut'],
      raw: true
    });

    res.status(200).json({
      success: true,
      data: {
        date,
        statistics: stats
      }
    });

  } catch (error) {
    console.error('Error in getDailyStats:', error);
    res.status(500).json({
      success: false,
      message: 'Erreur lors de la récupération des statistiques',
      error: error.message
    });
  }
};

module.exports = {
  getAllCommandes,
  getCommandeById,
  createCommande,
  updateCommande,
  deleteCommande,
  updateStatut,
  getCommandesByStatut,
  getDailyStats
};
