import { Application } from 'express-serve-static-core';
import bodyParser from 'body-parser';
import { Strategy as LocalStrategy } from 'passport-local';
import passport from 'passport';
import cors from 'cors';
import { User } from '../../data/models/user';
import rateLimiter, { extraRateLimit } from '../modules/rate-limiter';
import multer from 'multer';
import { extname, resolve } from 'path';
import crypto from 'crypto';
import { promisify } from 'util';
import { readFile, rename } from 'fs';
import validateUser from '../middleware/validate-user';
import updateUser from '../middleware/update-user';
import { execSync } from 'child_process';
import expressSession from 'express-session';
import MongoStore from 'connect-mongo';

const renameAsync = promisify(rename);
const readFileAsync = promisify(readFile);

function setupMulter(app: Application) {
  const uploadDir = resolve('./assets/upload');
  try {
    execSync(`mkdir -p ${uploadDir} 2>> /dev/null`);
  } catch {
    log.error("Upload folder not created. Refer to the setup guide.");
  }

  // uses storage rather than memory - 2 file operations per file upload
  const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, uploadDir),
    filename: (req, file, cb) => cb(null, Date.now() + extname(file.originalname)),
  });
  const upload = multer({
    storage,
    fileFilter: (req, file, callback) => {
      const fileSize = parseInt(req.headers['content-length'] as string);
      if (fileSize > 1024 * 1024)
        return callback(new Error('File size must be less than 1MB'));

      callback(null, true);
    },
    limits: { fileSize: 1024 * 1024 }
  });

  app.post('/v2/upload', updateUser, validateUser, extraRateLimit(10), upload.single('file'), async (req, res) => {
    try {
      if (!req.file) {
        throw new Error('No file uploaded');
      }
  
      const buffer = await readFileAsync(req.file.path);
      const hash = crypto.createHash('md5').update(buffer).digest('hex');
      const newFileName = hash + extname(req.file.originalname);
  
      await renameAsync(req.file.path, `${resolve('./assets/upload')}/${newFileName}`);
      log.silly(`Uploaded ${newFileName}`);
  
      const url = `/upload/${newFileName}`;
      res.status(201).json({ hash, url });
    } catch (error) {
      log.error(`File upload error: ${error.message}`);
      res.status(500).json({ error: error.message || 'An internal server error occurred' });
    }
  });
  
  
}
function setupPassport(app: Application) {
  passport.use(new LocalStrategy(
    { usernameField: 'email' },
    (User as any).authenticate(),
  ));
  passport.serializeUser((User as any).serializeUser());
  passport.deserializeUser((User as any).deserializeUser());
}

export default (app: Application) => {
  app.use(cors());
  app.use(bodyParser.json());
  app.use(passport.initialize());
  app.use(expressSession({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false, 
    cookie: { 
      secure: process.env.NODE_ENV === 'prod',
      maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
    },
    store: new MongoStore({
      mongoUrl: process.env.MONGO_URI,
      collection: 'sessions'
    })
  }));
  app.use(rateLimiter);

  setupPassport(app);
  setupMulter(app);
}