/**
 * API Response Optimization Middleware
 * Implements response batching, field filtering, and pagination
 */
const logger = require("../config/logger");

/**
 * Enable response compression for API endpoints
 */
const enableCompression = (req, res, next) => {
  // Already handled by global compression middleware
  next();
};

/**
 * Add cache headers for GET requests
 * SAFEGUARD: Checks headers not already sent before setting
 */
const addCacheHeaders = (maxAge = 300) => {
  return (req, res, next) => {
    if (req.method === "GET" && !res.headersSent) {
      try {
        res.set({
          "Cache-Control": `public, max-age=${maxAge}`,
          Vary: "Accept-Encoding",
        });
      } catch (error) {
        logger.warn("Failed to set cache headers", { error: error.message });
      }
    }
    next();
  };
};

/**
 * Field filtering middleware
 * Allows clients to request only specific fields: ?fields=id,name,price
 * SAFEGUARD: Validates field names to prevent injection attacks
 */
const fieldFilter = (req, res, next) => {
  const originalJson = res.json.bind(res);

  res.json = function (data) {
    const fields = req.query.fields;

    if (!fields || !data || res.headersSent) {
      return originalJson(data);
    }

    try {
      // SAFEGUARD: Validate field names (alphanumeric, underscore, dot only)
      if (!/^[a-zA-Z0-9_.,\s]+$/.test(fields)) {
        logger.warn("Invalid field filter attempted", { fields });
        return originalJson(data);
      }

      const fieldList = fields
        .split(",")
        .map((f) => f.trim())
        .filter(Boolean);

      // SAFEGUARD: Limit number of fields
      if (fieldList.length > 50) {
        logger.warn("Too many fields requested", { count: fieldList.length });
        return originalJson(data);
      }

      const filterObject = (obj) => {
        if (!obj || typeof obj !== "object") return obj;

        const filtered = {};
        fieldList.forEach((field) => {
          if (field in obj) {
            filtered[field] = obj[field];
          }
        });
        return filtered;
      };

      if (Array.isArray(data)) {
        data = data.map(filterObject);
      } else if (data.success !== undefined && data.data) {
        // Handle wrapped responses
        if (Array.isArray(data.data)) {
          data.data = data.data.map(filterObject);
        } else {
          data.data = filterObject(data.data);
        }
      } else {
        data = filterObject(data);
      }

      return originalJson(data);
    } catch (error) {
      logger.error("Field filter error", { error: error.message });
      return originalJson(data);
    }
  };

  next();
};

/**
 * Pagination middleware
 * Adds pagination support: ?page=1&limit=20
 */
const paginate = (defaultLimit = 20, maxLimit = 100) => {
  return (req, res, next) => {
    const page = Math.max(1, parseInt(req.query.page) || 1);
    const limit = Math.min(
      maxLimit,
      Math.max(1, parseInt(req.query.limit) || defaultLimit)
    );
    const offset = (page - 1) * limit;

    req.pagination = {
      page,
      limit,
      offset,
      maxLimit,
    };

    // Helper to add pagination info to response
    res.paginate = (data, total) => {
      const totalPages = Math.ceil(total / limit);
      return res.json({
        success: true,
        data,
        pagination: {
          page,
          limit,
          total,
          totalPages,
          hasNext: page < totalPages,
          hasPrev: page > 1,
        },
      });
    };

    next();
  };
};

/**
 * Response time tracking
 * SAFEGUARD: Checks headers not sent before setting X-Response-Time header
 */
const trackResponseTime = (req, res, next) => {
  const start = Date.now();

  res.on("finish", () => {
    const duration = Date.now() - start;

    // Log slow requests
    if (duration > 1000) {
      logger.warn("Slow API request", {
        method: req.method,
        path: req.path,
        duration: `${duration}ms`,
        status: res.statusCode,
      });
    }

    // Add response time header only if headers haven't been sent
    if (!res.headersSent) {
      try {
        res.set("X-Response-Time", `${duration}ms`);
      } catch (error) {
        logger.debug("Could not set X-Response-Time header", {
          error: error.message,
        });
      }
    }
  });

  next();
};

/**
 * ETag generation for GET requests
 * SAFEGUARD: Checks headersSent before setting headers
 */
const generateETag = (req, res, next) => {
  if (req.method !== "GET") {
    return next();
  }

  const originalJson = res.json.bind(res);

  res.json = function (data) {
    try {
      // SAFEGUARD: Don't process if headers already sent
      if (res.headersSent) {
        return originalJson(data);
      }

      // Generate simple ETag from stringified data
      const dataStr = JSON.stringify(data);
      const etag = `W/"${Buffer.from(dataStr).length.toString(16)}"`;

      // Check if client has cached version
      if (req.headers["if-none-match"] === etag) {
        res.status(304).end();
        return;
      }

      res.set("ETag", etag);
      return originalJson(data);
    } catch (error) {
      logger.error("ETag generation error", { error: error.message });
      return originalJson(data);
    }
  };

  next();
};

/**
 * JSON response size optimization
 * Removes null values and compacts responses
 */
const optimizeJSON = (req, res, next) => {
  const originalJson = res.json.bind(res);

  res.json = function (data) {
    if (data && typeof data === "object") {
      data = removeNulls(data);
    }
    return originalJson(data);
  };

  next();
};

function removeNulls(obj) {
  if (Array.isArray(obj)) {
    return obj.map(removeNulls);
  }

  if (obj !== null && typeof obj === "object") {
    return Object.entries(obj).reduce((acc, [key, value]) => {
      if (value !== null && value !== undefined) {
        acc[key] = removeNulls(value);
      }
      return acc;
    }, {});
  }

  return obj;
}

/**
 * Batch request handler
 * Allows multiple API calls in a single request
 * POST /api/batch with body: { requests: [{ method, url, body }] }
 */
const batchHandler = async (req, res) => {
  const { requests } = req.body;

  if (!Array.isArray(requests) || requests.length === 0) {
    return res.status(400).json({
      success: false,
      error: "Invalid batch request format",
    });
  }

  if (requests.length > 10) {
    return res.status(400).json({
      success: false,
      error: "Maximum 10 requests per batch",
    });
  }

  const results = await Promise.allSettled(
    requests.map(async (request) => {
      try {
        // This would require implementation of internal request handling
        // For now, return a placeholder
        return {
          status: 200,
          data: { message: "Batch processing not fully implemented" },
        };
      } catch (error) {
        return {
          status: 500,
          error: error.message,
        };
      }
    })
  );

  res.json({
    success: true,
    results: results.map((result, index) => ({
      ...requests[index],
      ...result,
    })),
  });
};

module.exports = {
  enableCompression,
  addCacheHeaders,
  fieldFilter,
  paginate,
  trackResponseTime,
  generateETag,
  optimizeJSON,
  batchHandler,
};
