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 équipement (admin ou manage_equipment)
exports.createEquipment = 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 equipmentData = req.body.data;
    const equipmentId = equipmentData.id;

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

    // Vérifier unicité de l'ID
    const existingDoc = await db.collection("equipments").doc(equipmentId).get();
    if (existingDoc.exists) {
      res.status(409).json({error: "Equipment ID already exists"});
      return;
    }

    // Convertir les timestamps
    const dataToSave = helpers.deserializeTimestamps(equipmentData, [
      "createdAt", "updatedAt", "purchaseDate", "lastMaintenanceDate", "nextMaintenanceDate",
    ]);

    await db.collection("equipments").doc(equipmentId).set(dataToSave);

    res.status(201).json({id: equipmentId, message: "Equipment created successfully"});
  } catch (error) {
    logger.error("Error creating equipment:", error);
    res.status(500).json({error: error.message});
  }
};

// Mettre à jour un équipement
exports.updateEquipment = 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 {equipmentId, data} = req.body.data;

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

    if (!data || typeof data !== "object" || Object.keys(data).length === 0) {
      res.status(400).json({error: "Update data is required and must be a non-empty object"});
      return;
    }

    // Empêcher la modification de l'ID
    delete data.id;

    // Ajouter updatedAt
    data.updatedAt = admin.firestore.Timestamp.now();

    const dataToSave = helpers.deserializeTimestamps(data, [
      "purchaseDate", "lastMaintenanceDate", "nextMaintenanceDate",
    ]);

    await db.collection("equipments").doc(equipmentId).update(dataToSave);

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

// Supprimer un équipement
exports.deleteEquipment = 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 {equipmentId, forceDelete = false} = req.body.data;

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

    // Vérifier si l'équipement est utilisé dans des événements à venir
    const eventsSnapshot = await db.collection("events")
        .where("status", "!=", "CANCELLED")
        .get();

    const now = new Date();
    const upcomingEvents = [];

    for (const eventDoc of eventsSnapshot.docs) {
      const eventData = eventDoc.data();
      const assignedEquipment = eventData.assignedEquipment || [];

      if (!assignedEquipment.some((eq) => eq.equipmentId === equipmentId)) {
        continue;
      }

      let eventStart = null;
      if (eventData.StartDateTime) {
        eventStart = eventData.StartDateTime.toDate ?
          eventData.StartDateTime.toDate() :
          new Date(eventData.StartDateTime);
      }

      if (eventStart && eventStart > now) {
        upcomingEvents.push({
          eventId: eventDoc.id,
          eventName: eventData.Name || "",
          startDate: eventStart.toISOString(),
        });
      }
    }

    if (upcomingEvents.length > 0 && !forceDelete) {
      res.status(409).json({
        error: "FUTURE_EVENT_ASSIGNMENT: Cannot delete equipment because it is assigned to upcoming events",
        upcomingEvents,
      });
      return;
    }

    await db.collection("equipments").doc(equipmentId).delete();

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

// Récupérer un équipement par ID
exports.getEquipment = 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 || req.query;

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

    const doc = await db.collection("equipments").doc(equipmentId).get();

    if (!doc.exists) {
      res.status(404).json({error: "Equipment not found"});
      return;
    }

    let data = {id: doc.id, ...doc.data()};
    data = helpers.serializeTimestamps(data);
    data = helpers.serializeReferences(data);

    // Masquer les prix si pas de permission manage_equipment
    data = helpers.maskSensitiveFields(data, hasManageAccess);

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

// Récupérer plusieurs équipements par leurs IDs
exports.getEquipmentsByIds = 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 {equipmentIds} = req.body.data || {};

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

    // Limiter à 100 équipements max par requête
    if (equipmentIds.length > 100) {
      res.status(400).json({error: "Maximum 100 equipment IDs per request"});
      return;
    }

    // Récupérer tous les documents en parallèle
    const promises = equipmentIds.map((id) => db.collection("equipments").doc(id).get());
    const docs = await Promise.all(promises);

    const equipments = [];
    for (const doc of docs) {
      if (doc.exists) {
        let data = {id: doc.id, ...doc.data()};
        data = helpers.serializeTimestamps(data);
        data = helpers.serializeReferences(data);
        // Masquer les prix si pas de permission manage_equipment
        data = helpers.maskSensitiveFields(data, hasManageAccess);
        equipments.push(data);
      }
    }

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

// Mettre à jour uniquement le statut d'un équipement
exports.updateEquipmentStatusOnly = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const {equipmentId, status, availableQuantity} = req.body.data;

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

    // Vérifier les permissions
    const hasAccess = await auth.hasPermission(decodedToken.uid, "manage_equipment");
    if (!hasAccess) {
      res.status(403).json({error: "Forbidden: Requires manage_equipment permission"});
      return;
    }

    const updateData = {updatedAt: admin.firestore.Timestamp.now()};
    if (status) updateData.status = status;
    if (availableQuantity !== undefined) updateData.availableQuantity = availableQuantity;

    await db.collection("equipments").doc(equipmentId).update(updateData);

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

// Mettre à jour le statut de plusieurs équipements (pour préparation/retour)
exports.updateEquipmentStatus = async (req, res) => {
  try {
    const decodedToken = await auth.authenticateUser(req);
    const {eventId, updates} = req.body.data;

    if (!eventId || !updates || !Array.isArray(updates)) {
      res.status(400).json({error: "Event ID and updates array are required"});
      return;
    }

    // Vérifier que l'utilisateur est assigné à l'événement ou est admin
    const isAssigned = await auth.isAssignedToEvent(decodedToken.uid, eventId);
    const isAdminUser = await auth.isAdmin(decodedToken.uid);

    if (!isAssigned && !isAdminUser) {
      res.status(403).json({error: "Forbidden: Not assigned to this event"});
      return;
    }

    // Batch update
    const batch = db.batch();

    for (const update of updates) {
      const {equipmentId, status} = update;
      if (equipmentId && status) {
        const equipmentRef = db.collection("equipments").doc(equipmentId);
        batch.update(equipmentRef, {status});
      }
    }

    await batch.commit();

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

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

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

    if (!canManage && !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 category = params.category ? params.category.toUpperCase() : null;
    const status = params.status ? params.status.toUpperCase() : null;
    const rawSearchQuery = typeof params.searchQuery === "string" ? params.searchQuery.trim() : "";
    const searchQuery = rawSearchQuery ? rawSearchQuery.toLowerCase() : null;
    const compactSearchQuery = searchQuery ? searchQuery.replace(/[\s_-]+/g, "") : null;
    const sortBy = params.sortBy || "id";
    const sortOrder = params.sortOrder === "desc" ? "desc" : "asc";

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

    // Fast-path pour une recherche d'ID exact
    if (searchQuery && !startAfterId) {
      const exactIdCandidates = Array.from(new Set([
        rawSearchQuery,
        rawSearchQuery.toUpperCase(),
        rawSearchQuery.toLowerCase(),
      ].filter(Boolean)));

      for (const candidateId of exactIdCandidates) {
        const exactDoc = await db.collection("equipments").doc(candidateId).get();
        if (!exactDoc.exists) {
          continue;
        }

        const exactData = exactDoc.data() || {};
        const matchesCategory = !category || exactData.category === category;
        const matchesStatus = !status || exactData.status === status;
        if (!matchesCategory || !matchesStatus) {
          continue;
        }

        if (!canManage) {
          delete exactData.purchasePrice;
          delete exactData.rentalPrice;
        }

        const exactEquipment = {
          ...helpers.serializeTimestamps(exactData, ["purchaseDate", "nextMaintenanceDate", "lastMaintenanceDate", "createdAt", "updatedAt"]),
          id: exactDoc.id,
        };

        logger.info(`[getEquipmentsPaginated] Exact ID hit for ${exactDoc.id}`);
        res.status(200).json({
          equipments: [exactEquipment],
          hasMore: false,
          lastVisible: exactDoc.id,
          total: 1,
        });
        return;
      }

      // Compatibilité legacy
      for (const legacyId of exactIdCandidates) {
        let legacyIdQuery = db.collection("equipments").where("id", "==", legacyId);
        if (category) {
          legacyIdQuery = legacyIdQuery.where("category", "==", category);
        }
        if (status) {
          legacyIdQuery = legacyIdQuery.where("status", "==", status);
        }

        const legacySnapshot = await legacyIdQuery.limit(1).get();
        if (legacySnapshot.empty) {
          continue;
        }

        const exactDoc = legacySnapshot.docs[0];
        const exactData = exactDoc.data() || {};

        if (!canManage) {
          delete exactData.purchasePrice;
          delete exactData.rentalPrice;
        }

        const exactEquipment = {
          ...helpers.serializeTimestamps(exactData, ["purchaseDate", "nextMaintenanceDate", "lastMaintenanceDate", "createdAt", "updatedAt"]),
          id: exactDoc.id,
        };

        logger.info(`[getEquipmentsPaginated] Exact legacy ID hit for ${legacyId} -> ${exactDoc.id}`);
        res.status(200).json({
          equipments: [exactEquipment],
          hasMore: false,
          lastVisible: exactDoc.id,
          total: 1,
        });
        return;
      }
    }

    // Construire la requête Firestore
    let query = db.collection("equipments");

    if (category) {
      query = query.where("category", "==", category);
    }
    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("equipments").doc(startAfterId).get();
      if (startAfterDoc.exists) {
        query = query.startAfter(startAfterDoc);
      }
    }

    const timestampFields = ["purchaseDate", "nextMaintenanceDate", "lastMaintenanceDate", "createdAt", "updatedAt"];

    const mapEquipmentDoc = (doc) => {
      const data = {...(doc.data() || {})};

      if (!canManage) {
        delete data.purchasePrice;
        delete data.rentalPrice;
      }

      const legacyId = typeof data.id === "string" ? data.id : "";

      return {
        ...helpers.serializeTimestamps(data, timestampFields),
        id: doc.id,
        _legacyId: legacyId,
      };
    };

    const matchesSearchQuery = (equipment) => {
      const searchableText = [
        equipment.name || "",
        equipment.id || "",
        equipment._legacyId || "",
        equipment.model || "",
        equipment.brand || "",
        equipment.subCategory || "",
      ].join(" ").toLowerCase();

      if (searchableText.includes(searchQuery)) {
        return true;
      }

      if (!compactSearchQuery) {
        return false;
      }

      const compactSearchableText = searchableText.replace(/[\s_-]+/g, "");
      return compactSearchableText.includes(compactSearchQuery);
    };

    if (!searchQuery) {
      const snapshot = await query.limit(limit + 1).get();
      const rawDocCount = snapshot.docs.length;
      const hasMoreDocs = rawDocCount > limit;
      const docsToProcess = hasMoreDocs ? snapshot.docs.slice(0, limit) : snapshot.docs;

      const limitedEquipments = docsToProcess
          .map(mapEquipmentDoc)
          .map(({_legacyId, ...equipment}) => equipment);
      const lastVisible = limitedEquipments.length > 0 ? limitedEquipments[limitedEquipments.length - 1].id : null;

      logger.info(`[getEquipmentsPaginated] Firestore returned ${rawDocCount} docs, hasMore=${hasMoreDocs}`);
      logger.info(`[getEquipmentsPaginated] Returning ${limitedEquipments.length} equipments, hasMore=${hasMoreDocs}`);

      res.status(200).json({
        equipments: limitedEquipments,
        hasMore: hasMoreDocs,
        lastVisible,
        total: limitedEquipments.length,
      });
      return;
    }

    const searchBatchSize = Math.min(Math.max(limit * 10, limit), 200);
    const matchedEquipments = [];
    let scannedDocuments = 0;
    let searchQueryRef = query;
    let hasMoreMatches = false;
    let hasMoreDocsToScan = true;

    while (hasMoreDocsToScan && !hasMoreMatches) {
      const snapshot = await searchQueryRef.limit(searchBatchSize).get();

      if (snapshot.empty) {
        hasMoreDocsToScan = false;
        break;
      }

      scannedDocuments += snapshot.docs.length;

      for (const doc of snapshot.docs) {
        const equipment = mapEquipmentDoc(doc);
        if (!matchesSearchQuery(equipment)) {
          continue;
        }

        matchedEquipments.push(equipment);
        if (matchedEquipments.length > limit) {
          hasMoreMatches = true;
          break;
        }
      }

      if (hasMoreMatches) {
        break;
      }

      if (snapshot.docs.length < searchBatchSize) {
        hasMoreDocsToScan = false;
        break;
      }

      const lastDocInBatch = snapshot.docs[snapshot.docs.length - 1];
      searchQueryRef = query.startAfter(lastDocInBatch);
    }

    const limitedEquipments = matchedEquipments
        .slice(0, limit)
        .map(({_legacyId, ...equipment}) => equipment);
    const lastVisible = limitedEquipments.length > 0 ? limitedEquipments[limitedEquipments.length - 1].id : null;

    logger.info(`[getEquipmentsPaginated] Search scan read ${scannedDocuments} docs and found ${matchedEquipments.length} matches`);
    logger.info(`[getEquipmentsPaginated] Returning ${limitedEquipments.length} equipments, hasMore=${hasMoreMatches}`);

    res.status(200).json({
      equipments: limitedEquipments,
      hasMore: hasMoreMatches,
      lastVisible,
      total: limitedEquipments.length,
    });
  } catch (error) {
    logger.error("Error fetching paginated equipments:", error);
    res.status(500).json({error: error.message});
  }
};

// Recherche rapide d'équipements et containers pour l'autocomplétion
exports.quickSearch = 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 view_equipment permission"});
      return;
    }

    const params = req.method === "GET" ? req.query : (req.body?.data || {});
    const searchQuery = params.query?.toLowerCase() || "";
    const limit = Math.min(parseInt(params.limit) || 10, 50);
    const includeEquipments = params.includeEquipments !== "false";
    const includeContainers = params.includeContainers !== "false";

    if (!searchQuery || searchQuery.length < 2) {
      res.status(200).json({results: []});
      return;
    }

    const results = [];

    // Rechercher dans les équipements
    if (includeEquipments) {
      const equipmentSnapshot = await db.collection("equipments")
          .orderBy("id")
          .limit(limit * 2)
          .get();

      equipmentSnapshot.docs.forEach((doc) => {
        const data = doc.data();
        const searchableText = [
          data.name || "",
          doc.id || "",
          data.model || "",
          data.brand || "",
        ].join(" ").toLowerCase();

        if (searchableText.includes(searchQuery)) {
          results.push({
            type: "equipment",
            id: doc.id,
            name: data.name,
            category: data.category,
            model: data.model,
            brand: data.brand,
          });
        }
      });
    }

    // Rechercher dans les containers
    if (includeContainers) {
      const containerSnapshot = await db.collection("containers")
          .orderBy("id")
          .limit(limit * 2)
          .get();

      containerSnapshot.docs.forEach((doc) => {
        const data = doc.data();
        const searchableText = [
          data.name || "",
          doc.id || "",
        ].join(" ").toLowerCase();

        if (searchableText.includes(searchQuery)) {
          results.push({
            type: "container",
            id: doc.id,
            name: data.name,
            containerType: data.type,
          });
        }
      });
    }

    const limitedResults = results
        .sort((a, b) => {
          const aStarts = a.id.toLowerCase().startsWith(searchQuery);
          const bStarts = b.id.toLowerCase().startsWith(searchQuery);
          if (aStarts && !bStarts) return -1;
          if (!aStarts && bStarts) return 1;
          return 0;
        })
        .slice(0, limit);

    res.status(200).json({results: limitedResults});
  } catch (error) {
    logger.error("Error in quick search:", error);
    res.status(500).json({error: error.message});
  }
};
