const express = require("express");
const router = express.Router();
const { authenticateToken } = require("../middleware/auth");
const { uploadPresignLimiter } = require("../middleware/rateLimiter");
const s3Service = require("../services/s3Service");
const S3OwnershipService = require("../services/s3OwnershipService");
const { v4: uuidv4 } = require("uuid");
const logger = require("../utils/logger");
const MAX_BATCH_SIZE = 20;

/**
 * Middleware to check if S3 is enabled
 */
const requireS3Enabled = (req, res, next) => {
  if (!s3Service.isEnabled()) {
    return res.status(503).json({
      error: "File upload service is not available",
    });
  }
  next();
};

/**
 * POST /api/upload/presign
 * Get a presigned URL for uploading a single file to S3
 */
router.post(
  "/presign",
  authenticateToken,
  requireS3Enabled,
  uploadPresignLimiter,
  async (req, res, next) => {
    try {
      const { uploadType, contentType, fileName, fileSize } = req.body;

      // Validate required fields
      if (!uploadType || !contentType || !fileName || !fileSize) {
        return res.status(400).json({ error: "Missing required fields" });
      }

      const result = await s3Service.getPresignedUploadUrl(
        uploadType,
        contentType,
        fileName,
        fileSize
      );

      logger.info("Presigned URL generated", {
        userId: req.user.id,
        uploadType,
        key: result.key,
      });

      res.json(result);
    } catch (error) {
      if (error.message.includes("Invalid")) {
        return res.status(400).json({ error: error.message });
      }
      next(error);
    }
  }
);

/**
 * POST /api/upload/presign-batch
 * Get presigned URLs for uploading multiple files to S3
 * All files in a batch share the same UUID base for coordinated variant uploads
 */
router.post(
  "/presign-batch",
  authenticateToken,
  requireS3Enabled,
  uploadPresignLimiter,
  async (req, res, next) => {
    try {
      const { uploadType, files } = req.body;

      if (!uploadType || !files || !Array.isArray(files)) {
        return res.status(400).json({ error: "Missing required fields" });
      }

      if (files.length === 0) {
        return res.status(400).json({ error: "No files specified" });
      }

      if (files.length > MAX_BATCH_SIZE) {
        return res
          .status(400)
          .json({ error: "Maximum ${MAX_BATCH_SIZE} files per batch" });
      }

      // Validate each file has required fields
      for (const file of files) {
        if (!file.contentType || !file.fileName || !file.fileSize) {
          return res.status(400).json({
            error: "Each file must have contentType, fileName, and fileSize",
          });
        }
      }

      // Generate one shared UUID for all files in this batch
      const sharedBaseKey = uuidv4();

      const results = await Promise.all(
        files.map((f) =>
          s3Service.getPresignedUploadUrl(
            uploadType,
            f.contentType,
            f.fileName,
            f.fileSize,
            sharedBaseKey
          )
        )
      );

      logger.info("Batch presigned URLs generated", {
        userId: req.user.id,
        uploadType,
        count: results.length,
        baseKey: sharedBaseKey,
      });

      res.json({ uploads: results, baseKey: sharedBaseKey });
    } catch (error) {
      if (error.message.includes("Invalid")) {
        return res.status(400).json({ error: error.message });
      }
      next(error);
    }
  }
);

/**
 * POST /api/upload/confirm
 * Confirm that files have been uploaded to S3
 */
router.post(
  "/confirm",
  authenticateToken,
  requireS3Enabled,
  async (req, res, next) => {
    try {
      const { keys } = req.body;

      if (!keys || !Array.isArray(keys)) {
        return res.status(400).json({ error: "Missing keys array" });
      }

      if (keys.length === 0) {
        return res.status(400).json({ error: "No keys specified" });
      }

      const results = await Promise.all(
        keys.map(async (key) => ({
          key,
          exists: await s3Service.verifyUpload(key),
        }))
      );

      const confirmed = results.filter((r) => r.exists).map((r) => r.key);

      logger.info("Upload confirmation", {
        userId: req.user.id,
        confirmed: confirmed.length,
        total: keys.length,
      });

      // Only return confirmed keys, not which ones failed (prevents file existence probing)
      res.json({ confirmed, total: keys.length });
    } catch (error) {
      next(error);
    }
  }
);

/**
 * GET /api/upload/signed-url/*key
 * Get a signed URL for accessing private content (messages, condition-checks)
 * The key is the full path after /signed-url/ (e.g., "messages/uuid.jpg")
 */
router.get(
  "/signed-url/*key",
  authenticateToken,
  requireS3Enabled,
  async (req, res, next) => {
    try {
      // Express wildcard params may be string or array - handle both
      let key = req.params.key;
      if (Array.isArray(key)) {
        key = key.join("/");
      }
      if (!key || typeof key !== "string") {
        return res.status(400).json({ error: "Invalid key parameter" });
      }
      // Decode URL-encoded characters (e.g., %2F -> /)
      key = decodeURIComponent(key);

      // Only allow private folders to use signed URLs
      const isPrivate =
        key.startsWith("messages/") || key.startsWith("condition-checks/");
      if (!isPrivate) {
        return res
          .status(400)
          .json({ error: "Signed URLs only for private content" });
      }

      // Verify user is authorized to access this file
      const authResult = await S3OwnershipService.canAccessFile(
        key,
        req.user.id
      );
      if (!authResult.authorized) {
        logger.warn("Unauthorized signed URL request", {
          userId: req.user.id,
          key,
          reason: authResult.reason,
        });
        return res.status(403).json({ error: "Access denied" });
      }

      const url = await s3Service.getPresignedDownloadUrl(key);

      res.json({ url, expiresIn: 3600 });
    } catch (error) {
      next(error);
    }
  }
);

module.exports = router;
