import RedisStore from 'connect-redis';
import getStoreConstructor from 'connect-sqlite3';
import cors from 'cors';
import session from 'express-session';
import passport from 'passport';
import { Server } from 'socket.io';
import { getUserById } from '../controllers/users.js';
import { getRedisClient } from '../external-services/redis.js';
import expressAsync from '../server-utils/expressAsync.js';
import { getAsyncStorage } from '../server-utils/logger.js';
import { convertUrlParamsToAppPath } from '../utils/urlParamPassthrough.js';
import facebook from './facebook.js';
import fusionauth from './fusionauth.js';
import github from './github.js';
import google from './google.js';
import local from './local.js';
import musehub from './musehub.js';

export function userShortInfo(user) {
  if (!user) {
    return null;
  }
  return (
    user.id +
    (user.plan === 'Pro' ? 'p' : user.plan === 'Pro (Trial)' ? 't' : user.plan === 'Indie' ? 'i' : 'f') +
    (user.role === 'admin' ? 'a' : '')
  );
}

const updateUser = async (req, res, next) => {
  if (req.isAuthenticated()) {
    const user = await getUserById(req.session.passport.user.id);
    Object.assign(req.session.passport.user, user);
  }
  next();
};

const wrap = (middleware) => (socket, next) => middleware(socket.request, {}, next);

export default (app, socketIO: Server, landingUri, appUri) => {
  let sessionStore;
  if (process.env.SESSION_PROVIDER === 'redis') {
    const redisClient = getRedisClient();
    const prefix = (process.env.REDIS_SESSION_PREFIX || 'wtsess') + ':';
    sessionStore = new RedisStore({
      client: redisClient,
      prefix,
    });
  } else {
    const SQLiteStore = getStoreConstructor(session);
    sessionStore = new SQLiteStore({ db: 'sessions.db' });
  }
  const useSecureCookies = !!process.env.COOKIE_SECURE;
  const sessionMiddleware = session({
    key: process.env.COOKIE_SESSION_KEY || 'sid',
    secret: process.env.COOKIE_SECRET || 'dwelonmuskrock',
    resave: false,
    proxy: useSecureCookies, // required in production since we're behind ALB
    saveUninitialized: false,

    store: sessionStore,
    cookie: {
      domain: process.env.COOKIE_DOMAIN || 'localhost',
      maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days in ms
      secure: useSecureCookies,
    },
    rolling: true,
  });

  const loggerMiddleware = (req, res, next) => {
    const storage = getAsyncStorage();
    if (storage && req.user) {
      storage.userInfo = userShortInfo(req.user);
    }
    next();
  };

  app.use(sessionMiddleware);
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(loggerMiddleware);

  passport.serializeUser(function (user, done) {
    done(null, user);
  });

  passport.deserializeUser(function (obj, done) {
    done(null, obj);
  });

  socketIO.use(wrap(sessionMiddleware));
  socketIO.use(wrap(passport.initialize()));
  socketIO.use(wrap(passport.session()));
  socketIO.use(wrap(loggerMiddleware));

  app.use('/', expressAsync(updateUser));

  app.get('/auth/logout', (req, res, next) => {
    req.logout(function (err) {
      if (err) {
        return next(err);
      }
      res.redirect(landingUri);
    });
  });

  app.get(
    '/auth/whoami',
    cors({
      origin: [landingUri, appUri],
      credentials: true,
    }),
    expressAsync(async (req, res) => {
      if (req.user) {
        res.send(req.user);
      } else {
        res.send({ error: 'Not Authenticated' });
      }
    })
  );

  app.get(
    '/auth/redirect',
    cors({
      origin: [landingUri, appUri],
      credentials: true,
    }),
    expressAsync(async (req, res) => {
      if (req.user) {
        const projectId = req.query.project_id;
        const audioUuid = req.query.audioUuid;
        const bpm = req.query.bpm;
        const stems = req.query.stems;
        const duration = req.query.duration;
        const skillToOpen = req.query.skillToOpen;
        const redirectUri = convertUrlParamsToAppPath(appUri, {
          projectId,
          audioUuid,
          bpm,
          stems,
          duration,
          skillToOpen,
        });
        res.redirect(redirectUri);
      } else {
        res.send({ error: 'Not Authenticated' });
      }
    })
  );

  app.get(
    '/remix/redirect/:project_uuid',
    cors({
      origin: [landingUri, appUri],
      credentials: true,
    }),
    expressAsync(async (req, res) => {
      if (req.user) {
        const projectId = req.params.project_uuid;
        const redirectUri = convertUrlParamsToAppPath(appUri, { projectId });
        res.redirect(redirectUri);
      } else {
        res.redirect(landingUri + '?project_id=' + req.params.project_uuid + '&error=remixnoauth');
      }
    })
  );

  app.get(
    '/auth/messages',
    cors({
      origin: [landingUri, appUri],
      credentials: true,
    }),
    expressAsync(async (req, res) => {
      const msgs = req.session?.messages;
      if (!msgs) {
        return res.send([]);
      }
      res.send(msgs);
      delete req.session.messages;
      req.session.save();
    })
  );

  github(app, 'og.wavtool.com');
  google(app, landingUri, appUri);
  facebook(app, landingUri, appUri);
  local(app, landingUri, appUri);
  musehub(app, landingUri, appUri);
  fusionauth(app, landingUri, appUri);
};
