const admin = require("firebase-admin");
const db = admin.firestore();
const logger = require("firebase-functions/logger");
const auth = require("../utils/auth");
const helpers = require("../utils/helpers");

// Créer un container
exports.createContainer = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const containerData = req.body.data;
    const containerId = containerData.id;

    if (!containerId) {
      res.status(400).json({error: "Container ID is required"});
      return;
    }

    const existingDoc = await db.collection("containers").doc(containerId).get();
    if (existingDoc.exists) {
      res.status(409).json({error: "Container ID already exists"});
      return;
    }

    const dataToSave = helpers.deserializeTimestamps(containerData, ["createdAt", "updatedAt"]);

    await db.collection("containers").doc(containerId).set(dataToSave);

    res.status(201).json({id: containerId, message: "Container created successfully"});
  } catch (error) {
    logger.error("Error creating container:", error);
    res.status(500).json({error: error.message});
  }
};

// Mettre à jour un container
exports.updateContainer = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const {containerId, data} = req.body.data;

    if (!containerId) {
      res.status(400).json({error: "Container ID is required"});
      return;
    }

    delete data.id;
    data.updatedAt = admin.firestore.Timestamp.now();

    await db.collection("containers").doc(containerId).update(data);

    res.status(200).json({message: "Container updated successfully"});
  } catch (error) {
    logger.error("Error updating container:", error);
    res.status(500).json({error: error.message});
  }
};

// Supprimer un container
exports.deleteContainer = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const {containerId} = req.body.data;

    if (!containerId) {
      res.status(400).json({error: "Container ID is required"});
      return;
    }

    // Récupérer le container pour obtenir les équipements
    const containerDoc = await db.collection("containers").doc(containerId).get();
    if (containerDoc.exists) {
      const containerData = containerDoc.data();
      const equipmentIds = containerData.equipmentIds || [];

      // Retirer le container des parentBoxIds de chaque équipement
      for (const equipmentId of equipmentIds) {
        try {
          const equipmentDoc = await db.collection("equipments").doc(equipmentId).get();
          if (equipmentDoc.exists) {
            const equipmentData = equipmentDoc.data();
            const parentBoxIds = (equipmentData.parentBoxIds || []).filter((boxId) => boxId !== containerId);
            await db.collection("equipments").doc(equipmentId).update({
              parentBoxIds: parentBoxIds,
              updatedAt: admin.firestore.Timestamp.now(),
            });
          }
        } catch (err) {
          logger.error(`Error updating equipment ${equipmentId} when deleting container:`, err);
        }
      }
    }

    await db.collection("containers").doc(containerId).delete();

    res.status(200).json({message: "Container deleted successfully"});
  } catch (error) {
    logger.error("Error deleting container:", error);
    res.status(500).json({error: error.message});
  }
};

// Récupérer les containers contenant un équipement
exports.getContainersByEquipment = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasViewAccess = await auth.hasPermission(decodedToken.uid, "view_equipment");
    const hasManageAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasViewAccess && !hasManageAccess) {
      res.status(403).json({error: "Forbidden: Requires view_equipment permission"});
      return;
    }

    const {equipmentId} = req.body.data || {};

    if (!equipmentId) {
      res.status(400).json({error: "equipmentId is required"});
      return;
    }

    const snapshot = await db.collection("containers")
        .where("equipmentIds", "array-contains", equipmentId)
        .get();

    const containers = [];
    snapshot.forEach((doc) => {
      let data = {id: doc.id, ...doc.data()};
      data = helpers.serializeTimestamps(data);
      data = helpers.serializeReferences(data);
      containers.push(data);
    });

    res.status(200).json({containers});
  } catch (error) {
    logger.error("Error getting containers by equipment:", error);
    res.status(500).json({error: error.message});
  }
};

// Récupérer plusieurs containers par leurs IDs
exports.getContainersByIds = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasViewAccess = await auth.hasPermission(decodedToken.uid, "view_equipment");
    const hasManageAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasViewAccess && !hasManageAccess) {
      res.status(403).json({error: "Forbidden: Requires view_equipment permission"});
      return;
    }

    const {containerIds} = req.body.data || {};

    if (!containerIds || !Array.isArray(containerIds) || containerIds.length === 0) {
      res.status(400).json({error: "containerIds array is required and must not be empty"});
      return;
    }

    if (containerIds.length > 100) {
      res.status(400).json({error: "Maximum 100 container IDs per request"});
      return;
    }

    const promises = containerIds.map((id) => db.collection("containers").doc(id).get());
    const docs = await Promise.all(promises);

    const containers = [];
    for (const doc of docs) {
      if (doc.exists) {
        let data = {id: doc.id, ...doc.data()};
        data = helpers.serializeTimestamps(data);
        data = helpers.serializeReferences(data);
        containers.push(data);
      }
    }

    res.status(200).json({containers});
  } catch (error) {
    logger.error("Error getting containers by IDs:", error);
    res.status(500).json({error: error.message});
  }
};

// Ajouter un équipement à un container
exports.addEquipmentToContainer = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const {containerId, equipmentId, userId} = req.body.data;

    if (!containerId || !equipmentId) {
      res.status(400).json({error: "containerId and equipmentId are required"});
      return;
    }

    const containerDoc = await db.collection("containers").doc(containerId).get();
    if (!containerDoc.exists) {
      res.status(404).json({success: false, message: "Container non trouvé"});
      return;
    }

    const containerData = containerDoc.data();
    const equipmentIds = containerData.equipmentIds || [];

    if (equipmentIds.includes(equipmentId)) {
      res.status(400).json({success: false, message: "Cet équipement est déjà dans ce container"});
      return;
    }

    const equipmentDoc = await db.collection("equipments").doc(equipmentId).get();
    if (!equipmentDoc.exists) {
      res.status(404).json({success: false, message: "Équipement non trouvé"});
      return;
    }

    const equipmentData = equipmentDoc.data();
    const parentBoxIds = equipmentData.parentBoxIds || [];

    const warnings = [];
    if (parentBoxIds.length > 0) {
      const otherContainersPromises = parentBoxIds.map((boxId) =>
        db.collection("containers").doc(boxId).get(),
      );
      const otherContainersDocs = await Promise.all(otherContainersPromises);
      const otherNames = otherContainersDocs
          .filter((doc) => doc.exists)
          .map((doc) => doc.data().name);

      if (otherNames.length > 0) {
        warnings.push(`Attention : cet équipement est également dans les boites suivants : ${otherNames.join(", ")}`);
      }
    }

    await db.collection("containers").doc(containerId).update({
      equipmentIds: [...equipmentIds, equipmentId],
      updatedAt: admin.firestore.Timestamp.now(),
    });

    await db.collection("equipments").doc(equipmentId).update({
      parentBoxIds: [...parentBoxIds, containerId],
      updatedAt: admin.firestore.Timestamp.now(),
    });

    const history = containerData.history || [];
    const historyEntry = {
      timestamp: admin.firestore.Timestamp.now(),
      action: "equipment_added",
      equipmentId: equipmentId,
      newValue: equipmentId,
      userId: userId || decodedToken.uid,
    };

    const updatedHistory = [...history, historyEntry].slice(-100);

    await db.collection("containers").doc(containerId).update({
      history: updatedHistory,
    });

    res.status(200).json({
      success: true,
      message: "Équipement ajouté avec succès",
      warnings: warnings.length > 0 ? warnings[0] : null,
    });
  } catch (error) {
    logger.error("Error adding equipment to container:", error);
    res.status(500).json({success: false, message: error.message});
  }
};

// Retirer un équipement d'un container
exports.removeEquipmentFromContainer = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");

    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const {containerId, equipmentId, userId} = req.body.data;

    if (!containerId || !equipmentId) {
      res.status(400).json({error: "containerId and equipmentId are required"});
      return;
    }

    const containerDoc = await db.collection("containers").doc(containerId).get();
    if (!containerDoc.exists) {
      res.status(404).json({error: "Container non trouvé"});
      return;
    }

    const containerData = containerDoc.data();
    const equipmentIds = containerData.equipmentIds || [];

    const updatedEquipmentIds = equipmentIds.filter((id) => id !== equipmentId);

    await db.collection("containers").doc(containerId).update({
      equipmentIds: updatedEquipmentIds,
      updatedAt: admin.firestore.Timestamp.now(),
    });

    const equipmentDoc = await db.collection("equipments").doc(equipmentId).get();
    if (equipmentDoc.exists) {
      const equipmentData = equipmentDoc.data();
      const parentBoxIds = equipmentData.parentBoxIds || [];
      const updatedParentBoxIds = parentBoxIds.filter((id) => id !== containerId);

      await db.collection("equipments").doc(equipmentId).update({
        parentBoxIds: updatedParentBoxIds,
        updatedAt: admin.firestore.Timestamp.now(),
      });
    }

    const history = containerData.history || [];
    const historyEntry = {
      timestamp: admin.firestore.Timestamp.now(),
      action: "equipment_removed",
      equipmentId: equipmentId,
      previousValue: equipmentId,
      userId: userId || decodedToken.uid,
    };

    const updatedHistory = [...history, historyEntry].slice(-100);

    await db.collection("containers").doc(containerId).update({
      history: updatedHistory,
    });

    res.status(200).json({success: true});
  } catch (error) {
    logger.error("Error removing equipment from container:", error);
    res.status(500).json({error: error.message});
  }
};

// Récupérer les containers avec pagination et filtrage côté serveur
exports.getContainersPaginated = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);

    // Vérifier les permissions
    const canView = await auth.hasPermission(decodedToken.uid, "view_equipment");

    if (!canView) {
      res.status(403).json({error: "Forbidden: Requires equipment permissions"});
      return;
    }

    // Récupérer les paramètres de la requête
    const params = req.method === "GET" ? req.query : (req.body?.data || {});
    const limit = Math.min(parseInt(params.limit) || 20, 100);
    const startAfterId = params.startAfter || null;
    const type = params.type ? params.type.toUpperCase() : null;
    const status = params.status ? params.status.toUpperCase() : null;
    const searchQuery = params.searchQuery?.toLowerCase() || null;
    const category = params.category ? params.category.toUpperCase() : null;
    const sortBy = params.sortBy || "id";
    const sortOrder = params.sortOrder === "desc" ? "desc" : "asc";

    logger.info(`[getContainersPaginated] Params: limit=${limit}, startAfter=${startAfterId}, type=${type}, status=${status}, category=${category}, search=${searchQuery}`);

    let query = db.collection("containers");

    const queryLimit = (searchQuery || category) ? Math.min(limit * 10, 200) : limit;

    if (type) {
      query = query.where("type", "==", type);
    }
    if (status) {
      query = query.where("status", "==", status);
    }

    if (sortBy === "id") {
      query = query.orderBy(admin.firestore.FieldPath.documentId(), sortOrder);
    } else {
      query = query.orderBy(sortBy, sortOrder);
    }

    if (startAfterId) {
      const startAfterDoc = await db.collection("containers").doc(startAfterId).get();
      if (startAfterDoc.exists) {
        query = query.startAfter(startAfterDoc);
      }
    }

    query = query.limit(queryLimit + 1);

    const snapshot = await query.get();

    const rawDocCount = snapshot.docs.length;
    const hasMoreDocs = rawDocCount > queryLimit;
    const docsToProcess = hasMoreDocs ? snapshot.docs.slice(0, queryLimit) : snapshot.docs;

    let containers = docsToProcess.map((doc) => {
      const data = doc.data();
      return {
        id: doc.id,
        ...helpers.serializeTimestamps(data, ["createdAt", "updatedAt"]),
      };
    });

    const allEquipmentIds = new Set();
    containers.forEach((c) => {
      if (c.equipmentIds && Array.isArray(c.equipmentIds)) {
        c.equipmentIds.forEach((id) => allEquipmentIds.add(id));
      }
    });

    const equipmentMap = new Map();
    if (allEquipmentIds.size > 0) {
      const equipmentIdArray = Array.from(allEquipmentIds);
      const batchSize = 30;

      for (let i = 0; i < equipmentIdArray.length; i += batchSize) {
        const batch = equipmentIdArray.slice(i, i + batchSize);
        const equipmentSnapshot = await db.collection("equipments")
            .where(admin.firestore.FieldPath.documentId(), "in", batch)
            .get();

        equipmentSnapshot.docs.forEach((doc) => {
          const equipmentData = doc.data();
          equipmentMap.set(doc.id, {
            id: doc.id,
            ...helpers.serializeTimestamps(equipmentData),
          });
        });
      }
    }

    containers = containers.map((container) => ({
      ...container,
      equipment: (container.equipmentIds || [])
          .map((eqId) => equipmentMap.get(eqId))
          .filter((eq) => eq !== undefined),
    }));

    if (category) {
      containers = containers.filter((c) => {
        return c.equipment.some((eq) => eq.category === category);
      });
    }

    if (searchQuery) {
      containers = containers.filter((c) => {
        const searchableText = [
          c.name || "",
          c.id || "",
          ...(c.equipment || []).map((eq) => eq.name || ""),
        ].join(" ").toLowerCase();
        return searchableText.includes(searchQuery);
      });
    }

    const limitedContainers = containers.slice(0, limit);
    const lastVisible = limitedContainers.length > 0 ? limitedContainers[limitedContainers.length - 1].id : null;

    const totalEquipmentCount = limitedContainers.reduce((sum, c) => sum + (c.equipment?.length || 0), 0);
    logger.info(`[getContainersPaginated] Returning ${limitedContainers.length} containers with ${totalEquipmentCount} total equipment(s)`);

    limitedContainers.forEach((c) => {
      logger.info(`  - Container ${c.id}: ${c.equipment?.length || 0} equipment(s)`);
    });

    res.status(200).json({
      containers: limitedContainers,
      hasMore: containers.length > limit || hasMoreDocs,
      lastVisible,
      total: limitedContainers.length,
    });
  } catch (error) {
    logger.error("Error fetching paginated containers:", error);
    res.status(500).json({error: error.message});
  }
};
