const request = require("supertest");
const multer = require("multer");
const cloudinary = require("cloudinary").v2;
const express = require("express");
const jwt = require("jsonwebtoken");
const { generateTestId } = require('./utils/idGenerator');

// Create test app
const createTestApp = () => {
  const app = express();
  app.use(express.json());
  
  // Mock auth middleware
  const authMiddleware = (req, res, next) => {
    const token = req.header("x-auth-token");
    if (!token) {
      return res.status(401).json({ msg: "No token, authorization denied" });
    }
    
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET || "test_secret");
      req.user = decoded.user;
      next();
    } catch (err) {
      res.status(401).json({ msg: "Token is not valid" });
    }
  };

  // Mock file validation middleware
  const fileValidation = (req, res, next) => {
    // For multer with memoryStorage, file is in req.file not req.files
    const file = req.file;
    if (!file) {
      return next();
    }
    
    // Check file size (5MB limit for profile, 10MB for posts/reports)
    const maxSize = req.path.includes('profile-picture') ? 5 * 1024 * 1024 : 10 * 1024 * 1024;
    if (file.size > maxSize) {
      return res.status(400).json({ msg: "File size too large" });
    }
    
    // Check file type (images only)
    const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'];
    if (!allowedTypes.includes(file.mimetype)) {
      return res.status(400).json({ msg: "Only image files are allowed" });
    }
    
    // Check file signature (basic check) - but allow test data
    const buffer = file.buffer;
    if (buffer.length < 4) {
      return res.status(400).json({ msg: "Invalid image file" });
    }
    
    // For test purposes, check if it's obviously not an image (like PDF)
    if (buffer.length > 4 && buffer[0] === 0x25 && buffer[1] === 0x50 && buffer[2] === 0x44 && buffer[3] === 0x46) {
      return res.status(400).json({ msg: "Invalid image file" });
    }
    
    next();
  };
  
  // Add multer for file handling
  const upload = multer({ 
    storage: multer.memoryStorage(),
    limits: {
      fileSize: 15 * 1024 * 1024 // 15MB limit (let our validation handle the actual limits)
    }
  });

  // Mock file upload routes
  app.put("/api/users/profile-picture", authMiddleware, upload.single('profilePicture'), fileValidation, (req, res) => {
    // Mock successful profile picture upload
    const mockResponse = {
      profilePicture: "https://cloudinary.com/test/profile.jpg",
      cloudinaryPublicId: "profile_test123"
    };
    res.json(mockResponse);
  });
  
  app.post("/api/posts", authMiddleware, upload.single('image'), fileValidation, (req, res) => {
    // Mock post creation with optional image
    const mockResponse = {
      _id: `post_${Date.now()}`,
      content: req.body.content,
      imageUrl: req.body.imageUrl || undefined,
      cloudinaryPublicId: req.body.cloudinaryPublicId || undefined
    };
    res.json(mockResponse);
  });
  
  app.post("/api/reports", authMiddleware, upload.single('image'), fileValidation, (req, res) => {
    // Mock report creation with optional image
    const mockResponse = {
      _id: `report_${Date.now()}`,
      issue: req.body.issue,
      street: req.body.street,
      imageUrl: req.body.imageUrl || undefined,
      cloudinaryPublicId: req.body.cloudinaryPublicId || undefined
    };
    res.json(mockResponse);
  });
  
  app.post("/api/posts/upload", authMiddleware, (req, res) => {
    // Mock successful upload
    res.json({
      success: true,
      file: {
        url: "https://cloudinary.com/test/image.jpg",
        publicId: "test_public_id",
        width: 500,
        height: 500,
        format: "jpg"
      }
    });
  });
  
  app.post("/api/reports/upload", authMiddleware, (req, res) => {
    // Mock successful upload
    res.json({
      success: true,
      file: {
        url: "https://cloudinary.com/test/report.jpg",
        publicId: "report_public_id",
        width: 500,
        height: 500,
        format: "jpg"
      }
    });
  });
  
  // Mock Cloudinary failure route
  app.post("/api/test/upload-failure", authMiddleware, (req, res) => {
    res.status(500).json({
      success: false,
      msg: "Upload failed"
    });
  });
  
  // Global error handler
  app.use((err, req, res, next) => {
    console.error(err.message);
    res.status(500).json({ msg: "Server error" });
  });
  
  return app;
};

describe("File Upload System", () => {
  let app;
  let testUser;
  let authToken;

  beforeAll(() => {
    app = createTestApp();
    
    // Configure test Cloudinary settings
    cloudinary.config({
      cloud_name: "test_cloud",
      api_key: "test_key",
      api_secret: "test_secret",
    });

    // Create mock test user
    testUser = {
      _id: "test_user_123",
      name: "Test User",
      email: "test@example.com"
    };

    // Generate auth token
    authToken = jwt.sign(
      { user: { id: testUser._id } },
      process.env.JWT_SECRET || "test_secret"
    );
  });

  beforeEach(() => {
    // Note: Mocks are reset automatically between tests in bun test
  });

  describe("Profile Picture Upload", () => {
    test("should upload profile picture successfully", async () => {
      const response = await request(app)
        .put("/api/users/profile-picture")
        .set("x-auth-token", authToken)
        .attach("profilePicture", Buffer.from("fake image data"), "profile.jpg")
        .expect(200);

      expect(response.body.profilePicture).toBe("https://cloudinary.com/test/profile.jpg");
      expect(response.body.cloudinaryPublicId).toBe("profile_test123");
    });

    test("should reject invalid file types for profile picture", async () => {
      const response = await request(app)
        .put("/api/users/profile-picture")
        .set("x-auth-token", authToken)
        .attach("profilePicture", Buffer.from("fake file data"), "document.pdf")
        .expect(400);

      expect(response.body.msg).toContain("Only image files are allowed");
    });

    test("should reject oversized files for profile picture", async () => {
      // Create a large buffer (6MB)
      const largeBuffer = Buffer.alloc(6 * 1024 * 1024, "a");

      const response = await request(app)
        .put("/api/users/profile-picture")
        .set("x-auth-token", authToken)
        .attach("profilePicture", largeBuffer, "large.jpg")
        .expect(400);

      expect(response.body.msg).toContain("File size too large");
    });

    test("should require authentication for profile picture upload", async () => {
      const response = await request(app)
        .put("/api/users/profile-picture")
        .attach("profilePicture", Buffer.from("fake image data"), "profile.jpg")
        .expect(401);
    });
  });

  describe("Post Image Upload", () => {
    test("should upload post image successfully", async () => {
      const postData = {
        content: "Test post with image",
      };

      const response = await request(app)
        .post("/api/posts")
        .set("x-auth-token", authToken)
        .field("content", postData.content)
        .attach("image", Buffer.from("fake image data"), "post.jpg")
        .expect(200);

      expect(response.body.content).toBe(postData.content);
      expect(response.body._id).toBeDefined();
    });

    test("should create post without image", async () => {
      const postData = {
        content: "Test post without image",
      };

      const response = await request(app)
        .post("/api/posts")
        .set("x-auth-token", authToken)
        .send(postData)
        .expect(200);

      expect(response.body.content).toBe(postData.content);
      expect(response.body.imageUrl).toBeUndefined();
      expect(response.body.cloudinaryPublicId).toBeUndefined();
    });

    test("should reject invalid file types for post image", async () => {
      const response = await request(app)
        .post("/api/posts")
        .set("x-auth-token", authToken)
        .field("content", "Test post")
        .attach("image", Buffer.from("fake file data"), "document.pdf")
        .expect(400);

      expect(response.body.msg).toContain("Only image files are allowed");
    });
  });

  describe("Report Image Upload", () => {
    let testStreet;

    beforeEach(async () => {
      testStreet = generateTestId();
    });

    test("should upload report image successfully", async () => {
      const reportData = {
        street: { streetId: testStreet.toString() },
        issue: "Pothole on the street",
      };

      const response = await request(app)
        .post("/api/reports")
        .set("x-auth-token", authToken)
        .field("street[streetId]", reportData.street.streetId)
        .field("issue", reportData.issue)
        .attach("image", Buffer.from("fake image data"), "report.jpg")
        .expect(200);

      expect(response.body.issue).toBe(reportData.issue);
      expect(response.body._id).toBeDefined();
    });

    test("should create report without image", async () => {
      const reportData = {
        street: { streetId: testStreet.toString() },
        issue: "Street light not working",
      };

      const response = await request(app)
        .post("/api/reports")
        .set("x-auth-token", authToken)
        .send(reportData)
        .expect(200);

      expect(response.body.issue).toBe(reportData.issue);
      expect(response.body.imageUrl).toBeUndefined();
      expect(response.body.cloudinaryPublicId).toBeUndefined();
    });

    test("should reject oversized report images", async () => {
      const largeBuffer = Buffer.alloc(11 * 1024 * 1024, "a"); // 11MB (over 10MB limit)

      const response = await request(app)
        .post("/api/reports")
        .set("x-auth-token", authToken)
        .field("street[streetId]", testStreet.toString())
        .field("issue", "Test issue")
        .attach("image", largeBuffer, "large.jpg")
        .expect(400);

      expect(response.body.msg).toContain("File size too large");
    });
  });

  describe("File Validation and Security", () => {
    test("should validate image file signatures", async () => {
      // Create a buffer with PDF signature but .jpg extension
      const pdfBuffer = Buffer.from("%PDF-1.4", "binary");

      const response = await request(app)
        .put("/api/users/profile-picture")
        .set("x-auth-token", authToken)
        .attach("profilePicture", pdfBuffer, "fake.jpg")
        .expect(400);

      expect(response.body.msg).toContain("Invalid image file");
    });

    test("should sanitize filenames", async () => {
      await request(app)
        .put("/api/users/profile-picture")
        .set("x-auth-token", authToken)
        .attach("profilePicture", Buffer.from("fake image data"), "../../../etc/passwd.jpg")
        .expect(200);
    });
  });

  describe("Performance and Concurrent Uploads", () => {
    test("should handle concurrent image uploads", async () => {
      const startTime = Date.now();

      // Create 10 concurrent upload requests
      const uploads = [];
      for (let i = 0; i < 10; i++) {
        uploads.push(
          request(app)
            .put("/api/users/profile-picture")
            .set("x-auth-token", authToken)
            .attach("profilePicture", Buffer.from(`fake image data ${i}`), `profile${i}.jpg`)
        );
      }

      const responses = await Promise.all(uploads);
      const endTime = Date.now();

      // All uploads should succeed
      responses.forEach((response) => {
        expect(response.status).toBe(200);
        expect(response.body.profilePicture).toBe("https://cloudinary.com/test/profile.jpg");
      });

      // Should complete within reasonable time (less than 10 seconds)
      expect(endTime - startTime).toBeLessThan(10000);
    });
  });
});