import compression from 'compression';
import cookieParser from 'cookie-parser';
import cors from 'cors';
import express from 'express';
import subdomain from 'express-subdomain';
import useragent from 'express-useragent';
import helmet from 'helmet';
import http from 'http';
import https from 'https';
import morgan from 'morgan';
import path from 'path';
import { Server } from 'socket.io';
import * as url from 'url';
import initAuthRouter, { userShortInfo } from './auth/auth.js';
import { logger, runWithAsyncStorage } from './server-utils/logger.js';
import { installPlatformEventHandlers } from './utils/platformEventHandling.js';
import { readFileSync } from 'fs';

import Sentry from './external-services/sentry.js';

import next from 'next';
import routes from './routes.js';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

const appUri = process.env.APP_URI || 'http://localhost:3002';
const landingUri = process.env.LANDING_URI || 'http://localhost:3000';

const app = express();

const httpServer = (
  process.env.ENVIRONMENT === 'development'
    ? () =>
        https.createServer(
          {
            cert: readFileSync('/WavTool/frontend/certs/cert.pem'),
            key: readFileSync('/WavTool/frontend/certs/dev.pem'),
          },
          app
        )
    : () => http.createServer(app)
)();

app.use(Sentry.Handlers.requestHandler());

const asyncStorageInitMiddleware = (req, res, next) => {
  return runWithAsyncStorage({ remoteIP: req.headers['x-forwarded-for'] }, () => next());
};

app.use(asyncStorageInitMiddleware);

app.use(cookieParser());
morgan.token('wavtool-user', function (req, res) {
  return userShortInfo(req.user);
});
app.use(
  morgan(
    ':req[x-forwarded-for] :wavtool-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms',
    {
      skip: (req, res) => req.url === '/healthz' || req.url === '/healthz-reachable',
    }
  )
);
app.use(compression());

app.use('*', (req, res, next) => {
  if (!req.cookies.wavtooladmin && process.env.OFFLINE_FOR_MAINTENANCE) {
    res.sendFile(path.join(__dirname.replace('server/built', 'offline-for-maintenance'), 'index.html'));
  } else {
    next();
  }
});

app.use(
  cors({
    origin: [landingUri, appUri],
    credentials: true,
  })
);

const socketIO = new Server(httpServer, {
  cors: {
    origin: appUri,
    methods: ['GET', 'POST'],
    credentials: true,
  },
});

socketIO.use((socket, next) => {
  return asyncStorageInitMiddleware(socket.request, {}, next);
});

initAuthRouter(app, socketIO, landingUri, appUri);

const credentiallessHelmet = helmet({
  crossOriginOpenerPolicy: true,
  crossOriginEmbedderPolicy: { policy: 'credentialless' },
  contentSecurityPolicy: false,
});

const fallbackHelmet = helmet({
  crossOriginOpenerPolicy: true,
  crossOriginEmbedderPolicy: true,
  contentSecurityPolicy: false,
});

app.use(useragent.express());

app.use(
  subdomain('app', (req, res, next) => {
    if (
      (req.useragent.browser === 'Safari' || req.useragent.browser === 'Firefox') &&
      !req.url?.includes('force-credentialless')
    ) {
      return fallbackHelmet(req, res, next);
    } else {
      return credentiallessHelmet(req, res, next);
    }
  })
);

const staticMiddleware = express.static(__dirname.replace('server/built', 'frontend/build'), { maxAge: '1h' });

app.use((req, res, next) => {
  if (req.subdomains.includes('app')) {
    staticMiddleware(req, res, next);
  } else {
    next();
  }
});

// Initialise all the routes of the app.
routes(app, socketIO, appUri);

app.use(Sentry.Handlers.errorHandler());

installPlatformEventHandlers();

const appNext = next({ dev: process.env.NODE_ENV !== 'production' });
const handle = appNext.getRequestHandler();

// This will capture all routes that are defined by the nextJS app.
// NextJS defines it's routes based on the files in the `pages` directory.
await appNext.prepare();
app.get('*', (req, res) => {
  console.log('NextJS route GET', req.url, req.headers.host, req.headers['x-forwarded-for']);
  return handle(req, res);
});

httpServer.listen(3001);
logger.info('server listening on port 3001');
