const express = require('express');
const path = require('path');
const fs = require('fs').promises;
const rateLimit = require('express-rate-limit');
var multer = require('multer');

const app = express();
const PORT = process.env.PORT || 3000;

// Configuration
require('dotenv').config();

const UPLOAD_DIR = path.join(__dirname, 'uploads');
const SITES_DIR = path.join(__dirname, 'sites');
const MAX_FREE_SIZE = parseInt(process.env.MAX_FREE_SIZE) || 100 * 1024 * 1024; // 100MB for free tier
const MAX_PAID_SIZE = parseInt(process.env.MAX_PAID_SIZE) || 2 * 1024 * 1024 * 1024; // 2GB for paid tier
const RENTAL_DURATION = parseInt(process.env.RENTAL_DURATION) || 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const DOMAINS = process.env.DOMAINS ? process.env.DOMAINS.split(',') : [];

// Middleware
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', "unsafe-hashes");
  next();
});

app.use('/', express.static("./sites"))
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP, please try again later.'
});
app.use(limiter);

// Set view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// Ensure directories exist
async function ensureDirectories() {
  try {
    await fs.mkdir(UPLOAD_DIR, { recursive: true });
    await fs.mkdir(SITES_DIR, { recursive: true });
  } catch (error) {
    console.error('Error creating directories:', error);
  }
}

// Storage configuration for multer
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, UPLOAD_DIR);
  },
  filename: function (req, file, cb) {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({
  storage: storage,
  limits: {
    fileSize: MAX_FREE_SIZE // Default to free tier limit
  },
  fileFilter: function (req, file, cb) {
    // Allow zip and html files
    if (file.mimetype === 'application/zip' || file.originalname.endsWith('.zip') ||
        file.mimetype === 'text/html' || file.originalname.endsWith('.html')) {
      cb(null, true);
    } else {
      cb(new Error('Only .zip and .html files are allowed!'), false);
    }
  }
});

// Load rentals from file or initialize empty
let rentals = new Map();
async function loadRentals() {
  try {
    const data = await fs.readFile('rentals.json', 'utf8');
    const parsed = JSON.parse(data);
    rentals = new Map(Object.entries(parsed));
  } catch (error) {
    if (error.code !== 'ENOENT') {
      console.error('Error loading rentals:', error);
    }
  }
}

async function saveRentals() {
  try {
    const data = Object.fromEntries(rentals);
    await fs.writeFile('rentals.json', JSON.stringify(data, null, 2));
  } catch (error) {
    console.error('Error saving rentals:', error);
  }
}

// Helper functions
function getClientIP(req) {
  return req.headers['x-forwarded-for'] || req.connection.remoteAddress || req.socket.remoteAddress || (req.connection.socket ? req.connection.socket.remoteAddress : null);
}

function isRented(subdomain) {
  const rental = rentals.get(subdomain);
  if (!rental) return false;

  // Check if rental has expired
  if (Date.now() > rental.expiresAt) {
    rentals.delete(subdomain);
    saveRentals(); // Save changes after deletion
    return false;
  }

  return true;
}

function getRentedUser(subdomain) {
  const rental = rentals.get(subdomain);
  return rental ? rental.userIP : null;
}

// Routes

// Home page route
app.get('/', (req, res) => {
  res.render('home', {
    title: 'Home',
    isRented: false,
    rentedUser: null,
    domains: DOMAINS
  });
});

// Plans page route
app.get('/plans', (req, res) => {
  res.render('plans', {
    title: 'Plans',
    plans: {
      free: {
        name: 'Free Plan',
        duration: '1 day',
        storage: '100MB',
        price: 'Free'
      },
      paid: {
        name: 'Premium Plan',
        duration: 'Any duration',
        storage: '2GB',
        price: 'Paid'
      }
    }
  });
});

// Upload and rent route
app.post('/rent', upload.single('website'), async (req, res) => {
  try {
    var subdomain = 'website'
    if (!req.file) {
      return res.status(400).render('error', {
        message: 'Please upload a zip file'
      });
    }

    // Accept terms check
    if (!req.body.terms) {
      return res.status(400).render('error', {
        message: 'You must accept the terms of service'
      });
    }

    const userIP = getClientIP(req);

    // Check if subdomain is already taken
    if (isRented(subdomain)) {
      return res.status(400).render('error', {
        message: 'A site is already rented. Only one site can be rented at a time.'
      });
    }

    // Handle file upload
    const sitePath = path.join(SITES_DIR);
    await fs.mkdir(sitePath, { recursive: true });

    const filePath = req.file.path;
    const fileExt = path.extname(req.file.originalname).toLowerCase();

    if (fileExt === '.html') {
      // For HTML files, move directly to sites directory
      const targetPath = path.join(sitePath, 'index.html');
      await fs.rename(filePath, targetPath);
      console.log(`[Upload] HTML file uploaded to ${targetPath}`);
    } else if (fileExt === '.zip') {
      // For ZIP files, extract to sites directory
      const extract = require('unzipper');
      console.log(`[Extracting] Extracting website to ${sitePath}`);

      await new Promise((resolve, reject) => {
        const stream = require('fs').createReadStream(filePath)
          .pipe(extract.Extract({ path: sitePath }));

        stream.on('close', () => {
          console.log(`[Extracting] Extracted website to ${sitePath}`);
          resolve();
        });
        stream.on('error', reject);
      });

      // Clean up uploaded zip
      await fs.unlink(filePath);
    } else {
      throw new Error('Unsupported file type');
    }

    // Create rental record
    const expiresAt = Date.now() + RENTAL_DURATION;
    const expiresDate = new Date(expiresAt).toLocaleString();
    rentals.set('website', {
      userIP: userIP,
      sitePath: sitePath,
      expiresAt: expiresAt,
      createdAt: Date.now()
    });

    // Save rentals to file
    await saveRentals();

    console.log(`[Rent] Rented server up to ${expiresDate}`);

    res.redirect('/');
  } catch (error) {
    console.error('Error renting site:', error);
    res.status(500).render('error', {
      message: 'Error processing your request'
    });
  }
});

// Root route - serve rented site or home page
app.get('/*', async (req, res, next) => {
  try {
    // Priority 1: Check if site is rented and serve the site
    if (isRented('site')) {
      const rental = rentals.get('site');
      const sitePath = rental.sitePath;

      // Set the rented user header
      res.setHeader('X-Rented-User', rental.userIP);

      // Look for index.html in the site directory
      const indexPath = path.join(sitePath, 'index.html');
      try {
        await fs.access(indexPath);
        return res.sendFile(indexPath);
      } catch (error) {
        // No index.html found, try to serve other files or show 404
        const requestedPath = req.path === '/' ? 'index.html' : req.path;
        const filePath = path.join(sitePath, requestedPath);

        try {
          await fs.access(filePath);
          // Set appropriate content type based on file extension
          const ext = path.extname(filePath).toLowerCase();
          let contentType = 'text/plain';

          switch (ext) {
            case '.html':
              contentType = 'text/html';
              break;
            case '.css':
              contentType = 'text/css';
              break;
            case '.js':
              contentType = 'application/javascript';
              break;
            case '.jpg':
            case '.jpeg':
              contentType = 'image/jpeg';
              break;
            case '.png':
              contentType = 'image/png';
              break;
            case '.gif':
              contentType = 'image/gif';
              break;
            case '.svg':
              contentType = 'image/svg+xml';
              break;
            case '.json':
              contentType = 'application/json';
              break;
            case '.xml':
              contentType = 'application/xml';
              break;
            case '.pdf':
              contentType = 'application/pdf';
              break;
            case '.woff':
              contentType = 'font/woff';
              break;
            case '.woff2':
              contentType = 'font/woff2';
              break;
            case '.ttf':
              contentType = 'font/ttf';
              break;
            case '.otf':
              contentType = 'font/otf';
              break;
            case '.eot':
              contentType = 'application/vnd.ms-fontobject';
              break;
            case '.ico':
              contentType = 'image/x-icon';
              break;
            case '.webp':
              contentType = 'image/webp';
              break;
            case '.webmanifest':
              contentType = 'application/manifest+json';
              break;
          }

          res.setHeader('Content-Type', contentType);
          return res.sendFile(filePath);
        } catch (error) {
          // Priority 2: If rented but file not found, show 404
          return res.status(404).render('site-404', {
            subdomain: 'site',
            title: 'Page Not Found'
          });
        }
      }
    }

    // Priority 3: If not rented, show home page
    res.render('home', {
      title: 'Home',
      isRented: false,
      rentedUser: null
    });

  } catch (error) {
    console.error('Error serving site:', error);
    next(error);
  }
});

// API endpoint to check rental status
app.get('/api/rental/:subdomain', (req, res) => {
  const subdomain = req.params.subdomain;
  const rented = isRented(subdomain);
  const rentedUser = getRentedUser(subdomain);

  res.json({
    rented: rented,
    rentedUser: rented,
    subdomain: subdomain
  });
});

// Removed language switcher route - English only

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).render('error', {
    error: process.env.NODE_ENV === 'development' ? err : {},
    message: 'Something went wrong!'
  });
});

// 404 handler
app.use((req, res) => {
  res.status(404).render('404');
});

// Initialize and start server
async function startServer() {
  await ensureDirectories();
  await loadRentals();

  // Clean up expired rentals on startup
  for (const [subdomain, rental] of rentals.entries()) {
    if (Date.now() > rental.expiresAt) {
      try {
        await fs.rm(rental.sitePath, { recursive: true, force: true });
        rentals.delete(subdomain);
        console.log(`[Rent] Rent ended! Cleaned up ${subdomain}`);
      } catch (error) {
        console.error(`Error cleaning up expired rental ${subdomain}:`, error);
      }
    }
  }

  // Save rentals after cleanup
  await saveRentals();

  app.listen(PORT, () => {
    console.log(`Website hosting service running on port ${PORT}`);
    console.log(`Free tier: ${MAX_FREE_SIZE / (1024 * 1024)}MB limit`);
    console.log(`Paid tier: ${MAX_PAID_SIZE / (1024 * 1024)}MB limit`);
  });
}

if (require.main === module) {
  startServer();
}

module.exports = app;
