const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs').promises;
const { db, logActivity } = require('../database/db');
const { adminAuth } = require('../middleware/auth');
const { generateThumbnail } = require('../services/imageProcessor');
const { generatePhotoFilename } = require('../utils/filenameSanitizer');
const { escapeLikePattern } = require('../utils/sqlSecurity');
const router = express.Router();

// Get storage path from environment or default
const getStoragePath = () => process.env.STORAGE_PATH || path.join(__dirname, '../../../storage');

// Configure multer for file uploads
const storage = multer.diskStorage({
  destination: async (req, file, cb) => {
    console.log('Multer destination called for file:', file.originalname);
    const { eventId } = req.params;
    
    try {
      // Get event details
      const event = await db('events').where({ id: eventId }).first();
      if (!event) {
        console.error('Event not found in multer destination:', eventId);
        return cb(new Error('Event not found'));
      }
      
      // Store event in request for use in filename generation
      req.eventData = event;
      
      // Create destination path - now just event folder, no type subfolder
      const destPath = path.join(getStoragePath(), 'events/active', event.slug);
      console.log('Destination path:', destPath);
      
      // Ensure directory exists
      await fs.mkdir(destPath, { recursive: true });
      
      cb(null, destPath);
    } catch (error) {
      console.error('Error in multer destination:', error);
      cb(error);
    }
  },
  filename: async (req, file, cb) => {
    console.log('Multer filename called for file:', file.originalname);
    try {
      // Use temporary filename for now, will rename after getting category info
      const tempName = `temp_${Date.now()}_${Math.round(Math.random() * 1E9)}${path.extname(file.originalname)}`;
      console.log('Temp filename:', tempName);
      cb(null, tempName);
    } catch (error) {
      console.error('Error in multer filename:', error);
      cb(error);
    }
  }
});

const { validateFileType } = require('../utils/fileSecurityUtils');

const upload = multer({
  storage: storage,
  limits: {
    fileSize: 50 * 1024 * 1024, // 50MB limit per file
    files: 500, // Maximum 500 files
    // Set a reasonable field size limit to prevent memory issues
    fieldSize: 10 * 1024 * 1024, // 10MB for non-file fields
  },
  fileFilter: (req, file, cb) => {
    // Accept images only with proper validation
    const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp'];
    
    if (validateFileType(file.originalname, file.mimetype, allowedMimeTypes)) {
      return cb(null, true);
    } else {
      cb(new Error('Only JPEG, PNG and WebP images are allowed'));
    }
  }
});

const { createFileUploadValidator } = require('../utils/fileSecurityUtils');

// Create content validator middleware
const validateUploadContent = createFileUploadValidator({
  allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
  maxFileSize: 50 * 1024 * 1024,
  validateContent: true
});

// Upload photos for an event
// Increased limit to 500 files, but recommend chunked uploads for better performance
router.post('/:eventId/upload', adminAuth, (req, res, next) => {
  upload.array('photos', 500)(req, res, (err) => {
    if (err) {
      console.error('Multer error:', err);
      if (err instanceof multer.MulterError) {
        if (err.code === 'LIMIT_FILE_SIZE') {
          return res.status(400).json({ error: 'File too large. Maximum size is 50MB per file.' });
        }
        if (err.code === 'LIMIT_FILE_COUNT') {
          return res.status(400).json({ error: 'Too many files. Maximum 500 files per upload.' });
        }
        return res.status(400).json({ error: `Upload error: ${err.message}` });
      }
      return res.status(400).json({ error: err.message || 'Upload failed' });
    }
    next();
  });
}, validateUploadContent, async (req, res) => {
  try {
    const { eventId } = req.params;
    const { category_id } = req.body;
    
    console.log('Upload request received for event:', eventId);
    console.log('Body:', req.body);
    console.log('Files:', req.files ? req.files.length : 'none');
    console.log('File details:', req.files?.map(f => ({ name: f.originalname, size: f.size, mimetype: f.mimetype })));
    console.log('Category ID received:', category_id);
    
    // Verify event exists and admin has access
    const event = await db('events').where({ id: eventId }).first();
    if (!event) {
      console.error('Event not found:', eventId);
      return res.status(404).json({ error: 'Event not found' });
    }
    
    if (!req.files || req.files.length === 0) {
      console.error('No files in request. req.files:', req.files);
      console.error('Request body keys:', Object.keys(req.body));
      return res.status(400).json({ error: 'No files uploaded' });
    }
    
    // Parse category_id to number if provided
    const parsedCategoryId = category_id ? parseInt(category_id, 10) : null;
    
    // Get category details if provided
    let category = null;
    if (parsedCategoryId) {
      category = await db('photo_categories').where({ id: parsedCategoryId }).first();
      if (!category) {
        return res.status(400).json({ error: 'Invalid category' });
      }
    }
    
    const uploadedPhotos = [];
    const errors = [];
    
    // Process files in batches to optimize database operations
    const BATCH_SIZE = 10; // Process 10 files at a time for database operations
    
    for (let i = 0; i < req.files.length; i += BATCH_SIZE) {
      const batch = req.files.slice(i, i + BATCH_SIZE);
      
      // Start a single transaction for the batch
      const trx = await db.transaction();
      
      try {
        // Get initial counter for this batch
        let batchCounter = 1;
        if (category) {
          const categoryData = await trx('photo_categories')
            .where({ id: parsedCategoryId })
            .forUpdate()
            .first();
          batchCounter = (categoryData.photo_counter || 0) + 1;
        } else {
          const uncategorizedCount = await trx('photos')
            .where({ event_id: eventId })
            .whereNull('category_id')
            .count('id as count')
            .first();
          batchCounter = (uncategorizedCount.count || 0) + 1;
        }
        
        const batchPhotos = [];
        
        for (let fileIndex = 0; fileIndex < batch.length; fileIndex++) {
          const file = batch[fileIndex];
          const counter = batchCounter + fileIndex;
          
          try {
            // Generate new filename
            const extension = path.extname(file.originalname);
            const newFilename = generatePhotoFilename(
              event.event_name,
              category ? category.name : 'uncategorized',
              counter,
              extension
            );
            
            // Rename the file
            const oldPath = file.path;
            const newPath = path.join(path.dirname(oldPath), newFilename);
            await fs.rename(oldPath, newPath);
            
            // Update file object
            file.filename = newFilename;
            file.path = newPath;
            
            // Generate thumbnail with new filename
            const thumbnailPath = await generateThumbnail(file.path);
            
            // Calculate relative paths
            const storagePath = getStoragePath();
            const relativePath = path.relative(path.join(storagePath, 'events/active'), file.path);
            const relativeThumbPath = thumbnailPath;
            
            // Prepare photo data for batch insert
            batchPhotos.push({
              event_id: eventId,
              filename: file.filename,
              path: relativePath,
              thumbnail_path: relativeThumbPath,
              category_id: parsedCategoryId || null,
              type: 'individual',
              size_bytes: file.size
            });
          } catch (error) {
            console.error(`Error processing file ${file.originalname}:`, error);
            errors.push({ filename: file.originalname, error: error.message });
            // Delete the file if it was partially processed
            if (file.path) {
              try { await fs.unlink(file.path); } catch (e) {}
            }
          }
        }
        
        // Batch insert all photos from this batch
        if (batchPhotos.length > 0) {
          const insertedIds = await trx('photos').insert(batchPhotos).returning('id');
          
          // Update category counter if needed
          if (category) {
            await trx('photo_categories')
              .where({ id: parsedCategoryId })
              .update({ photo_counter: batchCounter + batchPhotos.length - 1 });
          }
          
          // Add to uploaded photos array
          batchPhotos.forEach((photo, index) => {
            uploadedPhotos.push({
              id: insertedIds[index]?.id || insertedIds[index],
              filename: photo.filename,
              size: photo.size_bytes,
              category_id: photo.category_id
            });
          });
        }
        
        // Commit the batch transaction
        await trx.commit();
      } catch (error) {
        console.error(`Error processing batch starting at index ${i}:`, error);
        await trx.rollback();
        
        // Try to clean up files from failed batch
        for (const file of batch) {
          if (file.path) {
            try { await fs.unlink(file.path); } catch (e) {}
          }
        }
      }
    }
    
    // Log activity
    await logActivity('photos_uploaded',
      { count: uploadedPhotos.length, eventName: event.event_name },
      eventId,
      { type: 'admin', id: req.admin.id, name: req.admin.username }
    );
    
    // Prepare response
    const response = {
      message: `Successfully uploaded ${uploadedPhotos.length} photos`,
      photos: uploadedPhotos,
      totalFiles: req.files.length,
      successCount: uploadedPhotos.length,
      failureCount: errors.length
    };
    
    // Include error details if any files failed
    if (errors.length > 0) {
      response.errors = errors;
      response.message = `Uploaded ${uploadedPhotos.length} of ${req.files.length} photos. ${errors.length} failed.`;
    }
    
    res.json(response);
  } catch (error) {
    console.error('Error uploading photos:', error);
    res.status(500).json({ error: 'Failed to upload photos' });
  }
});

// Delete a photo
router.delete('/:eventId/photos/:photoId', adminAuth, async (req, res) => {
  try {
    const { eventId, photoId } = req.params;
    
    // Get photo details
    const photo = await db('photos')
      .where({ id: photoId, event_id: eventId })
      .first();
    
    if (!photo) {
      return res.status(404).json({ error: 'Photo not found' });
    }
    
    // Delete physical files
    const storagePath = getStoragePath();
    const photoPath = path.join(storagePath, 'events/active', photo.path);
    
    try {
      await fs.unlink(photoPath);
    } catch (error) {
      console.error('Error deleting photo file:', error);
    }
    
    // Delete thumbnail if exists
    if (photo.thumbnail_path) {
      const thumbPath = path.join(storagePath, 'events/active', photo.thumbnail_path);
      try {
        await fs.unlink(thumbPath);
      } catch (error) {
        console.error('Error deleting thumbnail:', error);
      }
    }
    
    // Remove from database
    await db('photos').where({ id: photoId }).delete();
    
    // Log activity
    const event = await db('events').where({ id: eventId }).first();
    await logActivity('photo_deleted',
      { filename: photo.filename, eventName: event.event_name },
      eventId,
      { type: 'admin', id: req.admin.id, name: req.admin.username }
    );
    
    res.json({ message: 'Photo deleted successfully' });
  } catch (error) {
    console.error('Error deleting photo:', error);
    res.status(500).json({ error: 'Failed to delete photo' });
  }
});

// Update a photo (e.g., change category)
router.patch('/:eventId/photos/:photoId', adminAuth, async (req, res) => {
  try {
    const { eventId, photoId } = req.params;
    const { category_id } = req.body;
    
    // Verify photo belongs to event
    const photo = await db('photos')
      .where({ id: photoId, event_id: eventId })
      .first();
    
    if (!photo) {
      return res.status(404).json({ error: 'Photo not found' });
    }
    
    // Update photo
    await db('photos')
      .where({ id: photoId })
      .update({ category_id: category_id || null });
    
    res.json({ message: 'Photo updated successfully' });
  } catch (error) {
    console.error('Error updating photo:', error);
    res.status(500).json({ error: 'Failed to update photo' });
  }
});

// Bulk delete photos
router.post('/:eventId/photos/bulk-delete', adminAuth, async (req, res) => {
  try {
    const { eventId } = req.params;
    const { photoIds } = req.body;
    
    if (!Array.isArray(photoIds) || photoIds.length === 0) {
      return res.status(400).json({ error: 'Invalid photo IDs' });
    }
    
    // Get all photos to delete
    const photos = await db('photos')
      .whereIn('id', photoIds)
      .where('event_id', eventId);
    
    if (photos.length === 0) {
      return res.status(404).json({ error: 'No photos found' });
    }
    
    // Delete physical files
    const storagePath = getStoragePath();
    const event = await db('events').where({ id: eventId }).first();
    
    for (const photo of photos) {
      // Delete photo file
      const photoPath = path.join(storagePath, 'events/active', photo.path);
      try {
        await fs.unlink(photoPath);
      } catch (error) {
        console.error('Error deleting photo file:', error);
      }
      
      // Delete thumbnail
      if (photo.thumbnail_path) {
        const thumbPath = path.join(storagePath, photo.thumbnail_path);
        try {
          await fs.unlink(thumbPath);
        } catch (error) {
          console.error('Error deleting thumbnail:', error);
        }
      }
    }
    
    // Delete from database
    await db('photos')
      .whereIn('id', photoIds)
      .where('event_id', eventId)
      .delete();
    
    // Log activity
    await logActivity('photos_bulk_deleted',
      { count: photos.length, eventName: event.event_name },
      eventId,
      { type: 'admin', id: req.admin.id, name: req.admin.username }
    );
    
    res.json({ message: `${photos.length} photos deleted successfully` });
  } catch (error) {
    console.error('Error bulk deleting photos:', error);
    res.status(500).json({ error: 'Failed to delete photos' });
  }
});

// Bulk update photos
router.post('/:eventId/photos/bulk-update', adminAuth, async (req, res) => {
  try {
    const { eventId } = req.params;
    const { photoIds, updates } = req.body;
    
    if (!Array.isArray(photoIds) || photoIds.length === 0) {
      return res.status(400).json({ error: 'Invalid photo IDs' });
    }
    
    // Verify all photos belong to the event
    const photoCount = await db('photos')
      .whereIn('id', photoIds)
      .where('event_id', eventId)
      .count('id as count')
      .first();
    
    if (photoCount.count !== photoIds.length) {
      return res.status(400).json({ error: 'Some photos do not belong to this event' });
    }
    
    // Update photos
    const updateData = {};
    if (updates.category_id !== undefined) {
      updateData.category_id = updates.category_id || null;
    }
    
    await db('photos')
      .whereIn('id', photoIds)
      .where('event_id', eventId)
      .update(updateData);
    
    res.json({ message: `${photoIds.length} photos updated successfully` });
  } catch (error) {
    console.error('Error bulk updating photos:', error);
    res.status(500).json({ error: 'Failed to update photos' });
  }
});

// Download a photo
router.get('/:eventId/photos/:photoId/download', adminAuth, async (req, res) => {
  try {
    const { eventId, photoId } = req.params;
    
    const photo = await db('photos')
      .where({ id: photoId, event_id: eventId })
      .first();
    
    if (!photo) {
      return res.status(404).json({ error: 'Photo not found' });
    }
    
    const storagePath = getStoragePath();
    const filePath = path.join(storagePath, 'events/active', photo.path);
    
    // Check if file exists
    try {
      await fs.access(filePath);
    } catch (error) {
      return res.status(404).json({ error: 'Photo file not found' });
    }
    
    // Send file
    res.download(filePath, photo.filename);
  } catch (error) {
    console.error('Error downloading photo:', error);
    res.status(500).json({ error: 'Failed to download photo' });
  }
});

// Get all photos for an event
router.get('/:eventId/photos', adminAuth, async (req, res) => {
  try {
    const { eventId } = req.params;
    const { category_id, type, search, sort = 'date', order = 'desc' } = req.query;
    
    let query = db('photos')
      .leftJoin('photo_categories', 'photos.category_id', 'photo_categories.id')
      .where({ 'photos.event_id': eventId })
      .select(
        'photos.*',
        'photo_categories.name as category_name',
        'photo_categories.slug as category_slug'
      );
    
    // Filter by category (including uncategorized)
    if (category_id !== undefined) {
      if (category_id === '' || category_id === '0') {
        query = query.whereNull('photos.category_id');
      } else {
        query = query.where({ 'photos.category_id': category_id });
      }
    }
    
    // Keep type filter for backwards compatibility
    if (type) {
      query = query.where({ 'photos.type': type });
    }
    
    // Search by filename
    if (search) {
      const escapedSearch = escapeLikePattern(search);
      query = query.where('photos.filename', 'like', `%${escapedSearch}%`);
    }
    
    // Sorting
    let orderByColumn = 'photos.uploaded_at';
    if (sort === 'name') {
      orderByColumn = 'photos.filename';
    } else if (sort === 'size') {
      orderByColumn = 'photos.size_bytes';
    }
    
    const photos = await query.orderBy(orderByColumn, order);
    
    res.json({
      photos: photos.map(photo => ({
        id: photo.id,
        filename: photo.filename,
        url: `/admin/events/${eventId}/photo/${photo.id}`,
        thumbnail_url: photo.thumbnail_path ? `/admin/events/${eventId}/thumbnail/${photo.id}` : null,
        type: photo.type,
        category_id: photo.category_id,
        category_name: photo.category_name,
        category_slug: photo.category_slug,
        size: photo.size_bytes,
        uploaded_at: photo.uploaded_at
      }))
    });
  } catch (error) {
    console.error('Error fetching photos:', error);
    res.status(500).json({ error: 'Failed to fetch photos' });
  }
});

// Serve photo with admin authentication
router.get('/:eventId/photo/:photoId', adminAuth, async (req, res) => {
  try {
    const { eventId, photoId } = req.params;
    
    const photo = await db('photos')
      .where({ id: photoId, event_id: eventId })
      .first();
    
    if (!photo) {
      return res.status(404).json({ error: 'Photo not found' });
    }
    
    const storagePath = getStoragePath();
    const filePath = path.join(storagePath, 'events/active', photo.path);
    
    // Check if file exists
    try {
      await fs.access(filePath);
    } catch (error) {
      return res.status(404).json({ error: 'Photo file not found' });
    }
    
    // Set appropriate headers
    res.setHeader('Content-Type', `image/${path.extname(photo.filename).slice(1)}`);
    res.setHeader('Cache-Control', 'private, max-age=3600');
    res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
    
    // Send file (sendFile requires absolute path)
    res.sendFile(path.resolve(filePath));
  } catch (error) {
    console.error('Error serving photo:', error);
    res.status(500).json({ error: 'Failed to serve photo' });
  }
});

// Serve thumbnail with admin authentication
router.get('/:eventId/thumbnail/:photoId', adminAuth, async (req, res) => {
  try {
    const { eventId, photoId } = req.params;
    
    const photo = await db('photos')
      .where({ id: photoId, event_id: eventId })
      .first();
    
    if (!photo || !photo.thumbnail_path) {
      console.error(`Thumbnail not found for photo ${photoId}, event ${eventId}`);
      return res.status(404).json({ error: 'Thumbnail not found' });
    }
    
    const storagePath = getStoragePath();
    const filePath = path.join(storagePath, photo.thumbnail_path);
    
    console.log(`Attempting to serve thumbnail: ${filePath}`);
    
    // Check if file exists
    try {
      await fs.access(filePath);
    } catch (error) {
      console.error(`Thumbnail file not found: ${filePath}`, error);
      return res.status(404).json({ error: 'Thumbnail file not found' });
    }
    
    // Set appropriate headers
    res.setHeader('Content-Type', `image/${path.extname(photo.thumbnail_path).slice(1)}`);
    res.setHeader('Cache-Control', 'private, max-age=3600');
    res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
    
    // Send file (sendFile requires absolute path)
    res.sendFile(path.resolve(filePath));
  } catch (error) {
    console.error('Error serving thumbnail:', error);
    console.error('Photo ID:', req.params.photoId);
    console.error('Event ID:', req.params.eventId);
    res.status(500).json({ error: 'Failed to serve thumbnail' });
  }
});

// Debug endpoint to check photo existence
router.get('/:eventId/debug', adminAuth, async (req, res) => {
  try {
    const { eventId } = req.params;
    
    const event = await db('events').where({ id: eventId }).first();
    const photoCount = await db('photos').where({ event_id: eventId }).count('id as count').first();
    const photos = await db('photos').where({ event_id: eventId }).limit(5);
    
    res.json({
      event: event || 'Not found',
      photoCount: photoCount.count,
      samplePhotos: photos,
      storagePath: getStoragePath()
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;