require('dotenv').config();
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
const path = require('path');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');
const cookieParser = require('cookie-parser');
const compression = require('compression');

// Import database connection
const { connectDB } = require('./config/db');

// Import and initialize models
require('./models');

// Import routes
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
const brandRoutes = require('./routes/brands');
const categoryRoutes = require('./routes/categories');
const itemRoutes = require('./routes/items');
const orderRoutes = require('./routes/orders');
const storeRoutes = require('./routes/stores');
const deliveryBoyRoutes = require('./routes/deliveryBoys');
const bannerRoutes = require('./routes/banners');
const prescriptionsRouter = require('./routes/prescriptions');
const addressRoutes = require('./routes/addresses');
const notificationRoutes = require('./routes/notifications');
const settingsRoutes = require('./routes/settings');
const uploadRoutes = require('./routes/upload');
const unitRoutes = require('./routes/units');
const taxRoutes = require('./routes/taxes');
const discountRoutes = require('./routes/discounts');
const locationRoutes = require('./routes/location');
const pickerPackerRoutes = require('./routes/pickerPacker');
const cartRoutes = require('./routes/cart');
const apiRoutes = require('./routes/api');

// Import error handlers
const { errorHandler, notFound } = require('./middleware/errorHandler');

// Initialize express app
const app = express();

// =====================
// GLOBAL MIDDLEWARES
// =====================

// Increase request payload size limit (50MB)
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true, parameterLimit: 50000 }));

// Set up file system and directories
const fs = require('fs');
const uploadsDir = path.join(__dirname, 'uploads');
const bannersDir = path.join(uploadsDir, 'banners');

// Create directories if they don't exist
if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir);
if (!fs.existsSync(bannersDir)) fs.mkdirSync(bannersDir);

// Serve static files from /uploads
app.use('/uploads', express.static(uploadsDir, {
  setHeaders: (res, path) => {
    // Set proper cache control for static files
    res.setHeader('Cache-Control', 'public, max-age=31536000');
  }
}));

// Serve static files from uploads directory
app.use('/api/uploads', express.static(path.join(__dirname, 'public/uploads'), {
  setHeaders: (res, path) => {
    // Set proper content type based on file extension
    const ext = path.split('.').pop().toLowerCase();
    if (['png', 'jpg', 'jpeg', 'gif'].includes(ext)) {
      res.setHeader('Content-Type', `image/${ext === 'jpg' ? 'jpeg' : ext}`);
    }
  }
}));

// Serve prescription files from uploads/prescriptions directory
app.use('/api/uploads/prescriptions', express.static(path.join(__dirname, 'uploads/prescriptions'), {
  setHeaders: (res, path) => {
    // Set proper content type based on file extension
    const ext = path.split('.').pop().toLowerCase();
    if (['png', 'jpg', 'jpeg', 'gif', 'pdf'].includes(ext)) {
      res.setHeader('Content-Type', ext === 'pdf' ? 'application/pdf' : `image/${ext === 'jpg' ? 'jpeg' : ext}`);
    }
  }
}));

// Handle /api/uploads/banners/... requests
app.use('/api/uploads/banners', (req, res, next) => {
  const filename = req.path.split('/').pop();
  res.redirect(302, `/uploads/banners/${filename}`);
});

// Handle legacy /api/uploadsbanners/... requests (without the slash)
app.use('/api/uploadsbanners', (req, res, next) => {
  const filename = req.path.split('/').pop();
  res.redirect(302, `/uploads/banners/${filename}`);
});

// Connect to MongoDB
connectDB();
app.use(helmet());

// Parse allowed origins from environment variable
const allowedOrigins = process.env.FRONTEND_URLS ? 
  process.env.FRONTEND_URLS.split(',').map(url => url.trim()) : 
  ['http://localhost:3000', 'http://localhost:5173'];

// CORS configuration middleware
app.use((req, res, next) => {
  const origin = req.headers.origin;
  
  // Allow requests with no origin (like mobile apps or curl requests)
  if (allowedOrigins.includes(origin) || !origin) {
    // Set CORS headers - include both lowercase and title-case versions of headers
    res.setHeader('Access-Control-Allow-Origin', origin || '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, X-CSRF-Token, x-lat, x-lng, X-Lat, X-Lng');
    res.setHeader('Access-Control-Expose-Headers', 'Content-Range, X-Total-Count, x-lat, x-lng, X-Lat, X-Lng');
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    
    // Handle preflight requests
    if (req.method === 'OPTIONS') {
      return res.status(200).end();
    }
  }
  
  next();
});

// Development logging
if (process.env.NODE_ENV === 'development') {
  app.use(morgan('dev'));
}

// Limit requests from same API
const limiter = rateLimit({
  max: process.env.RATE_LIMIT_MAX || (process.env.NODE_ENV === 'development' ? 1000 : 100),
  windowMs: process.env.NODE_ENV === 'development' ? 15 * 60 * 1000 : 60 * 60 * 1000, // 15 minutes in dev, 1 hour in prod
  message: 'Too many requests from this IP, please try again later!'
});
app.use('/api', limiter);

// Body parser, reading data from body into req.body
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
app.use(cookieParser());

// Data sanitization against NoSQL query injection
app.use(mongoSanitize());

// Data sanitization against XSS
app.use(xss());

// Prevent parameter pollution
app.use(
  hpp({
    whitelist: [
      'duration',
      'ratingsQuantity',
      'ratingsAverage',
      'maxGroupSize',
      'difficulty',
      'price'
    ]
  })
);

// Compression middleware
app.use(compression());

// Test middleware
app.use((req, res, next) => {
  req.requestTime = new Date().toISOString();
  next();
});

// Enable pre-flight across-the-board
app.options('*', cors());

// =====================
// ROUTES
// =====================

// Mount API v1 routes
app.use('/api/v1', apiRoutes);

// Mount additional routes that aren't in the main API router
app.use('/api/v1/auth', authRoutes);
app.use('/api/v1/users', userRoutes);
app.use('/api/v1/brands', brandRoutes);
app.use('/api/v1/categories', categoryRoutes);
app.use('/api/items', itemRoutes);
app.use('/api/orders', orderRoutes);
app.use('/api/stores', storeRoutes);
app.use('/api/delivery-boys', deliveryBoyRoutes);
app.use('/api/banners', bannerRoutes);
app.use('/api/addresses', addressRoutes);
app.use('/api/notifications', notificationRoutes);
app.use('/api/settings', settingsRoutes);
app.use('/api/units', unitRoutes);
app.use('/api/taxes', taxRoutes);
app.use('/api/discounts', discountRoutes);
app.use('/api/location', locationRoutes);
app.use('/api/picker-packers', pickerPackerRoutes);
app.use('/api/cart', cartRoutes);

// Mount additional versioned API routes
app.use('/api/v1/prescriptions', prescriptionsRouter);

// Serve uploaded files with proper caching headers
app.use('/uploads', (req, res, next) => {
  // Set cache control headers (1 day)
  res.set('Cache-Control', 'public, max-age=86400');
  next();
}, express.static(path.join(__dirname, 'public/uploads'), {
  // Enable fallthrough to allow 404 handling
  fallthrough: true,
  // Set etag and last-modified headers
  etag: true,
  lastModified: true,
  // Don't redirect to add trailing slash
  redirect: false
}));

// Handle 404 for uploaded files
app.use('/uploads', (req, res) => {
  // If we get here, the file wasn't found
  res.status(404).json({
    status: 'error',
    message: 'File not found'
  });
});

// =====================
// ERROR HANDLING
// =====================
app.use(notFound);
app.use(errorHandler);

// Export the app
module.exports = app;
