import express, { RequestHandler } from 'express';
import session from 'express-session';
import passport from 'passport';
import * as redis from 'redis';
import * as icynetstrat from 'passport-icynet';
import connectRedis from 'connect-redis';

import http from 'http';
import { join } from 'path';
import { Server } from 'socket.io';
import { IcyNetUser } from '../common/types/user';
import {
  ServerToClientEvents,
  ClientToServerEvents,
} from '../common/types/socket';

import { config } from './config';
import { Game } from './game';
import { InterServerEvents, SocketData } from './types/socket';

const RedisStore = connectRedis(session);
const redisClient = config.redis?.enabled ? redis.createClient({
  url: config.redis?.url || 'redis://localhost:6379',
}) : undefined;

const sessionMiddleware = session({
  secret: config.server.sessionSecret,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: process.env.NODE_ENV === 'production', sameSite: 'strict' },
  store: config.redis?.enabled
    ? new RedisStore({ client: redisClient })
    : undefined,
});

// todo: store less info in session
passport.serializeUser((user, done) => {
  done(null, user);
});

passport.deserializeUser((obj: IcyNetUser, done) => {
  done(null, obj);
});

passport.use(
  new icynetstrat.Strategy(
    {
      clientID: config.auth.clientID,
      clientSecret: config.auth.clientSecret,
      callbackURL: config.auth.callbackURL,
      scope: [],
    },
    function (
      accessToken: string,
      refreshToken: string,
      profile: any,
      done: Function,
    ) {
      process.nextTick(function () {
        return done(null, profile);
      });
    },
  ),
);

const app = express();

if (process.env.NODE_ENV === 'production') {
  app.enable('trust proxy');
}

const server = http.createServer(app);
const io = new Server<
  ClientToServerEvents,
  ServerToClientEvents,
  InterServerEvents,
  SocketData
>(server);

const checkAuth: RequestHandler = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next();
  }
  res.send('not logged in :(');
};

app.use(sessionMiddleware);

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

app.get(
  '/login',
  passport.authenticate('icynet', { scope: [] }),
  (req, res) => {},
);

app.get(
  '/callback',
  passport.authenticate('icynet', { failureRedirect: '/?login=false' }),
  (req, res) => {
    res.redirect('/?login=true');
  }, // auth success
);

app.get('/logout', (req, res) => {
  req.logout();
  res.redirect('/');
});

app.get('/info', checkAuth, (req, res) => {
  res.json(req.user);
});

app.use('/assets', express.static(join(__dirname, '..', '..', 'assets')));
app.use(express.static(join(__dirname, '..', 'public')));

///

const game = new Game(io, sessionMiddleware);

game.initialize().then(() =>
  server.listen(config.server.port, config.server.bind, () => {
    console.log(`Listening at http://localhost:${config.server.port}/`);
  }),
);
