const express = require('express');
const { Auth, AuthType } = require('@qlik/sdk');

let prevHost = null;
let prevClientId = null;
let authInstance = null;

const getAuthInstance = (returnToOrigin, host, clientId) => {
  if (authInstance && prevHost === host && prevClientId === clientId) {
    return authInstance;
  }

  prevHost = host;
  prevClientId = clientId;

  authInstance = new Auth({
    authType: AuthType.OAuth2,
    host,
    clientId,
    redirectUri: `${returnToOrigin}/auth/login/callback`,
  });
  return authInstance;
};

const OAuthRouter = ({ originUrl }) => {
  const router = express.Router();

  let cachedHost = null;
  let cachedClientId = null;

  router.get('/oauth', async (req, res) => {
    const { host: qHost, clientId: qClientId } = req.query;
    if (!cachedHost && !cachedClientId) {
      cachedHost = qHost;
      cachedClientId = qClientId;
    }

    const returnTo = `${req.protocol}://${req.get('host')}`;
    const instacne = getAuthInstance(returnTo, qHost, qClientId);
    const isAuthorized = await instacne.isAuthorized();
    if (!isAuthorized) {
      const { url: redirectUrl } = await instacne.generateAuthorizationUrl();
      res.status(200).json({ redirectUrl });
    } else {
      const reqHost = req.get('host');
      const redirectUrl = `${req.protocol}://${reqHost}/app-list?engine_url=wss://${cachedHost}&qlik-client-id=${cachedClientId}&shouldFetchAppList=true`;

      cachedHost = null;
      cachedClientId = null;

      res.redirect(redirectUrl);
    }
  });

  router.get('/login/callback', async (req, res) => {
    const authLink = new URL(req.url, `http://${req.headers.host}`).href;
    try {
      // TODO:
      // this is a temp fix in nebula side
      // (temp workaround of not presisting origin while backend tries to authorize user)
      // they need to handle this in qlik-sdk-typescript repo
      // and will notify us about when they got fixed it,
      // but until then, we need to take care of it here!
      authInstance.rest.interceptors.request.use((_req) => {
        // eslint-disable-next-line no-param-reassign, dot-notation
        _req[1]['headers'] = { origin: originUrl };
        return _req;
      });
      await authInstance.authorize(authLink);
      res.redirect(301, `/auth/oauth?host=${cachedHost}&clientId=${cachedClientId}`);
    } catch (err) {
      console.log({ err });
      res.status(401).send({
        message: 'Auth failed in callback redirecting, based on the provided auth link!',
      });
    }
  });

  router.get('/deauthorize', async (req, res) => {
    try {
      prevHost = null;
      prevClientId = null;
      authInstance = null;

      cachedHost = null;
      cachedClientId = null;
      await authInstance?.deauthorize();

      res.status(200).json({
        deauthorize: true,
      });
    } catch (error) {
      console.log({ error });
    }
  });

  router.get('/isAuthorized', async (req, res) => {
    if (!authInstance) {
      res.status(200).json({
        isAuthorized: false,
      });
    } else {
      const isAuthorized = await authInstance.isAuthorized();
      res.status(200).json({
        isAuthorized,
      });
    }
  });

  router.get('/getSocketUrl/:appId', async (req, res) => {
    const { appId } = req.params;
    const webSocketUrl = await authInstance.generateWebsocketUrl(appId, true);
    res.status(200).json({ webSocketUrl });
  });

  return router;
};

const getAvailableAuthInstance = () => authInstance;

module.exports = { OAuthRouter, getAvailableAuthInstance };
