import dotenv from 'dotenv';
import log4js from 'log4js';
import mongoose from 'mongoose';
import { getAllModels, getModelByPrefix } from './model.js';
dotenv.config();

const logger = log4js.getLogger('Spotlight');
logger.level = process.env.LOG_LEVEL;

// Helper function to build search filter from query parameters
const buildSearchFilter = (params) => {
  const filter = {};

  for (const [key, value] of Object.entries(params)) {
    // Skip pagination and limit parameters as they're not search filters
    if (key === 'limit' || key === 'page') continue;

    // Handle different field types
    if (key === 'name') {
      filter.name = { $regex: value, $options: 'i' }; // Case-insensitive search
    } else if (key === 'id' || key === '_id') {
      if (mongoose.Types.ObjectId.isValid(value)) {
        filter._id = value;
      }
    } else if (key === 'tags') {
      filter.tags = { $in: [new RegExp(value, 'i')] };
    } else if (key === 'state') {
      filter['state.type'] = value;
    } else if (key.includes('.')) {
      // Handle nested fields like 'state.type', 'address.city', etc.
      filter[key] = { $regex: value, $options: 'i' };
    } else {
      // For all other fields, do a case-insensitive search
      filter[key] = { $regex: value, $options: 'i' };
    }
  }

  return filter;
};

const trimSpotlightObject = (object, objectType) => {
  return {
    _id: object._id,
    name: object.name || undefined,
    state: object.state && object?.state.type ? { type: object.state.type } : undefined,
    tags: object.tags || undefined,
    email: object.email || undefined,
    color: object.color || undefined,
    updatedAt: object.updatedAt || undefined,
    objectType: objectType || undefined,
    online: object.online || undefined,
  };
};

export const getSpotlightRouteHandler = async (req, res) => {
  try {
    const query = req.params.query;
    const queryParams = req.query;
    if (query.length < 3) {
      res.status(200).send([]);
      return;
    }
    const prefix = query.substring(0, 3).toUpperCase();
    const delimiter = query.substring(3, 4);
    const suffix = query.substring(4);

    logger.trace(`Spotlight query: ${query}`);

    if (delimiter == ':') {
      const prefixEntry = getModelByPrefix(prefix);
      if (!prefixEntry || !prefixEntry.model) {
        res.status(400).send({ error: 'Invalid or unsupported prefix' });
        return;
      }
      const { model, idField, type, referenceField } = prefixEntry;

      const suffixLength = suffix.length;

      // Validate ObjectId if the idField is '_id'
      if (
        idField === '_id' &&
        referenceField === '_reference' &&
        !mongoose.Types.ObjectId.isValid(suffix) &&
        suffixLength != 12
      ) {
        res.status(200).send([]);
        return;
      }

      // Find the object by the correct field
      const queryObj = {};

      if (suffixLength == 12) {
        queryObj[referenceField] = suffix.toUpperCase();
      } else {
        queryObj[idField] = suffix.toLowerCase();
      }

      let doc = await model.findOne(queryObj).lean();

      if (!doc) {
        res.status(200).send([]);
        return;
      }
      // Build the response with only the required fields
      const response = trimSpotlightObject(doc, type);
      res.status(200).send(response);
      return;
    }

    console.log(queryParams);

    if (Object.keys(queryParams).length > 0) {
      const prefixEntry = getModelByPrefix(prefix);
      console.log(prefixEntry);
      if (!prefixEntry || !prefixEntry.model) {
        res.status(400).send({ error: 'Invalid or unsupported prefix' });
        return;
      }
      const { model, type } = prefixEntry;

      // Use req.query for search parameters

      if (Object.keys(queryParams).length === 0) {
        res.status(400).send({ error: 'No search parameters provided' });
        return;
      }

      // Build search filter
      const searchFilter = buildSearchFilter(queryParams);

      // Perform search with limit
      const limit = parseInt(req.query.limit) || 10;
      const docs = await model.find(searchFilter).limit(limit).sort({ updatedAt: -1 }).lean();

      // Format response
      const response = docs.map((doc) => trimSpotlightObject(doc, type));

      res.status(200).send(response);
      return;
    }

    // If no query params and no prefix, search all models
    if (Object.keys(queryParams).length === 0 && (!prefix || !getModelByPrefix(prefix))) {
      // Search all models for the query string in the 'name' field
      const searchTerm = query;
      if (!searchTerm || searchTerm.length < 3) {
        res.status(200).send([]);
        return;
      }
      // Only use models that are not null
      const allModelEntries = getAllModels().filter((entry) => entry.model);
      // Run all searches in parallel
      const searchPromises = allModelEntries.map(async (entry) => {
        try {
          const docs = await entry.model
            .find({ name: { $regex: searchTerm, $options: 'i' } })
            .limit(5)
            .sort({ updatedAt: -1 })
            .lean();
          return docs.map((doc) => trimSpotlightObject(doc, entry.type));
        } catch (e) {
          return [];
        }
      });
      let results = await Promise.all(searchPromises);
      // Flatten and deduplicate by _id
      let flatResults = results.flat();
      const seen = new Set();
      const deduped = [];
      for (const obj of flatResults) {
        if (!seen.has(String(obj._id))) {
          seen.add(String(obj._id));
          deduped.push(obj);
        }
        if (deduped.length >= 10) break;
      }
      res.status(200).send(deduped);
      return;
    }
    return res.status(200).send([]);
  } catch (error) {
    logger.error('Error in spotlight lookup:', error);
    res.status(500).send({ error: error });
  }
};
