const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const crypto = require('crypto');
const helmet = require('helmet');
const dotenv = require('dotenv');
const semusiConfig = require('./config')
dotenv.config({ path: './.env' });
const requestIp = require('request-ip');
const logger = require('../logger');
const indexRouter = require('./routes/index');
const cors = require('cors');
const bodyParser = require('body-parser');
const formData = require('express-form-data');

const app = express();



// Middleware for parsing form data
app.use(formData.parse('connect-multiparty'));
app.use(formData.format());
app.use(formData.union());

// Middleware for body parsing
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());


app.use(cookieParser()); // Use cookieParser 

// Set CSP and trust proxy
app.set('trust proxy', true );
app.use((req, res, next) => {
  res.locals.nonce = crypto.randomBytes(16).toString("hex");
  next();
});

app.use(function (req, res, next) {
  res.setHeader(
    'Content-Security-Policy',
    `default-src 'self'; font-src 'self' ${semusiConfig.fontsrc}; img-src 'self' blob: data: ${semusiConfig.imagesrc} ${semusiConfig.appListSrc}; script-src 'self' 'nonce-${res.locals.nonce}' 'unsafe-inline'  'strict-dynamic' https: http:; style-src 'self' ${semusiConfig.stylesrc}; frame-src 'self' ${semusiConfig.framesrc} ; object-src 'none'; base-uri 'self';connect-src 'self' ${semusiConfig.connectsrc};`
  );
  next();
});
// Security middleware
app.use(helmet.hidePoweredBy());
app.use(helmet.frameguard());
app.use(
  helmet.hsts({
    maxAge: 315569520000,   //max age is The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS (10years)
  })
);

app.use(helmet.noSniff());
app.use(helmet.referrerPolicy({ policy: 'no-referrer' }));
app.use(helmet.permittedCrossDomainPolicies());

// CORS configuration
app.use(cors({
  origin: semusiConfig.accessContolAllowOrigin,
  credentials:true
}));
// Middleware for parsing JSON and URL-encoded bodies
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// Custom middleware for content-length validation
app.use((req,res,next) =>{
  logs(req,res);
  if (req.method === 'GET') {
    let cl = parseInt(req.headers['content-length']) || 2;
    let  bl = [];
    const str = JSON.stringify(req.body);
    const buffer = Buffer.from(str, 'UTF-8');
    for (let i = 0; i < buffer.length; i++) {
      bl.push(buffer[i]);
    }
    if (cl === bl.length){
      next();
    } else {
      res.status(400).json({ error: 'Content-Length mismatch' });
    }
  } else {
    next();
  }
});

// Routes
app.use('/', indexRouter);


// Catch-all for undefined routes
 app.all('*', (req, res) => {
  res.status(404).json({
    success: false,
    data: '404'
  });
});

// Error handling
app.use((req, res, next) => {
  next(createError(404));
});

 // Restrict HTTP Methods
 app.use((req, res, next) => {
   const allowedMethods = ['GET', 'POST'];
   if (!allowedMethods.includes(req.method)) {
       return res.sendStatus(405); // Method Not Allowed
   }
   next();
 });
app.use(function(err, req, res, next) {

  
  res.setHeader('Cross-Origin-Resource-Policy', 'same-site');
            // Cross-Origin Resource Policy (CORP)
  // Ensures that resources are only shared with the same origin
  res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
  // Cross-Origin Opener Policy (COOP)
  // Prevents cross-origin interactions by isolating your site
  res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
  // Cross-Origin Embedder Policy (COEP)
  // Requires cross-origin resources to grant permission before being embedded
  res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
  // Permission Policy header
  // Controls access to various browser features and APIs
  res.setHeader('Permission-Policy',
    'geolocation=(self), ' +              // Restrict location access to your origin
    'microphone=(), ' +                  // Disallow microphone access
    'camera=(), ' +                      // Disallow camera access
    'fullscreen=(self), ' +              // Restrict fullscreen requests to your origin
    'payment=(self), ' +                 // Restrict Payment API to your origin
    'push=(self), ' +                    // Restrict Push API to your origin
    'clipboard-write=(), ' +             // Disallow clipboard write access
    'accelerometer=(), ' +               // Disallow accelerometer access
    'ambient-light-sensor=(), ' +        // Disallow ambient light sensor access
    'magnetometer=(), ' +                // Disallow magnetometer access
    'gyroscope=(), ' +                   // Disallow gyroscope access
    'usb=()'                             // Disallow WebUSB API access
  );
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.json({ error: err });
});



// Routes to trigger errors
app.get('/403', function(req, res, next){// trigger a 403 error
  const err = new Error('not allowed!');
  err.status = 403;
  next(err);
});
app.get('/500', function(req, res, next){// trigger a generic (500) error
  next(new Error('keyboard cat!'));
});

app.listen(process.env.serverPort1, ()=>{})

// Logging function
async function logs(req, res) {
  if (req.query.api_key) {
    try {
      const member = await common.db.collection('members').findOne({ 'api_key': req.query.api_key });
      if (member) {
        let email = member.email.split('');
        let finalArr = [];
        let len = email.indexOf('@');
        email.forEach((item, pos) => {
          (pos >= 1 && pos <= len - 2) ? finalArr.push('*') : finalArr.push(email[pos]);
        });
        const bytesAtBeginningOfRequest = req.socket.bytesWritten || 0;
        let bytesWritten = 0;
        let bytesServed = 0;
        req.socket.on('drain', function() {
          bytesWritten += req.socket.bytesWritten;
        });
        bytesServed = bytesWritten - bytesAtBeginningOfRequest < 0 ? bytesAtBeginningOfRequest : 0;
        logger.info({
          hostname: req.headers.host,
          userAgent: req.headers['user-agent'],
          PATH: req.url,
          URL: req.originalUrl,
          response_status: res.statusCode,
          user_id: finalArr.join(''),
          response: req.query,
          bytes_served: bytesServed,
          server_address: semusiConfig.api.host,
          memberId: req.query.api_key,
          client_IP: requestIp.getClientIp(req),
        });
      }
    } catch (error) {
      logger.error(`Error ${JSON.stringify(error)}`);
    }
  }
}

module.exports = app;
