import express from 'express';
import fs from 'node:fs';
import jwt from 'jsonwebtoken';

import log from '../util/logger';

import {JWT_TOKEN_SECRET} from '../util/secret';

/**
 * Load keys to sign token and verify it.
 * In a real app the authentication would be performed on a separate server.
 */
let privateKey: string;
let publicKey: string;

try {
  log.info('Loading keys');
  publicKey = fs.readFileSync('./key/jwt.pub', 'utf8');
  privateKey = fs.readFileSync('./key/jwt.key', 'utf8');
  log.info('Loaded keys');
} catch (err) {
  log.error('Unable to load keys');
  process.exit(-1);
}

/**
 * Define sample JWT controller.
 */
export const routerJWT = express.Router();

/**
 * Sign JWT using a secret, return using a cookie.
 */
routerJWT.post('/login/secret', (req, res) => {
  try {
    const user = req.body.name;
    const jwtToken = jwt.sign(
      {name: user, exp: Math.floor(Date.now() / 1000) + 5},
      JWT_TOKEN_SECRET,
    );
    res.cookie('jwt', jwtToken);
    res.json({jwt: jwtToken});
  } catch (err) {
    log.error('Failed to sign key');
  }
});

/**
 * Sign JWT using a private SSL key, return using a cookie.
 */
routerJWT.post('/login/key', (req, res) => {
  try {
    const user = req.body.name;
    const jwtToken = jwt.sign(
      {name: user, exp: Math.floor(Date.now() / 1000) + 20},
      privateKey,
      {algorithm: 'RS256'},
    );
    res.cookie('jwt', jwtToken);
    res.json({jwt: jwtToken});
  } catch (err) {
    log.error('Failed to sign key');
  }
});

/**
 * Verify JWT using a secret, using a cookie.
 */
routerJWT.get('/protected/secret', (req, res) => {
  const jwtToken = req.cookies.jwt ?? '';
  log.info('jwt: ', jwtToken);
  try {
    const jwtDecoded = jwt.verify(jwtToken, JWT_TOKEN_SECRET);

    // if token alg != RS256,  err == invalid signature
    if (jwtDecoded && jwtDecoded['name'] === 'Rajinder') {
      res.send('Passed');
    } else {
      log.error('Invalid User');
      res.status(403).send('Invalid User');
    }
  } catch (ex) {
    log.error('Invalid User');
    res.status(403).send('Invalid User');
  }
});

/**
 * Verify JWT using a SSL public key, using a cookie.
 */
routerJWT.get('/protected/key', (req, res) => {
  const jwtToken = req.cookies.jwt ?? '';
  log.info('jwt: ', jwtToken);
  jwt.verify(
    jwtToken,
    publicKey,
    {algorithms: ['RS256']},
    function (err, jwtDecoded) {
      // if token alg != RS256,  err == invalid signature
      if (jwtDecoded && jwtDecoded['name'] === 'Rajinder') {
        res.send('Passed');
      } else {
        log.error('Invalid User');
        res.status(403).send('Invalid User');
      }
    },
  );
});

/**
 * Verify JWT using a SSL public key, using bearer header.
 * Authorization: Bearer xx.yy.zz
 */
routerJWT.get('/protected/bearer', (req, res) => {
  const bearerToken = req.get('Authorization');
  const tok = (bearerToken && bearerToken.split(' ')) ?? [];
  const jwtToken = tok[1];

  log.info('jwt: ', jwtToken);
  jwt.verify(
    jwtToken,
    publicKey,
    {algorithms: ['RS256']},
    function (err, jwtDecoded) {
      // if token alg != RS256,  err == invalid signature
      if (jwtDecoded && jwtDecoded['name'] === 'Rajinder') {
        res.send('Passed');
      } else {
        log.error('Invalid User');
        res.status(403).send('Invalid User');
      }
    },
  );
});
