/**
 * This file contains the Node.js code, used for the optional status check feature
 * It accepts a single url parameter, and will make an empty GET request to that
 * endpoint, and then resolve the response status code, time taken, and short message
 */
const axios = require('axios').default;
const https = require('https');

/* Determines if successful from the HTTP response code */
const getResponseType = (code, validCodes) => {
  if (validCodes && String(validCodes).includes(String(code))) return true;
  if (Number.isNaN(code)) return false;
  const numericCode = parseInt(code, 10);
  return (numericCode >= 200 && numericCode <= 302);
};

/* Makes human-readable response text for successful check */
const makeMessageText = (data) => `${data.successStatus ? '✅' : '⚠️'} `
  + `${data.serverName || 'Server'} responded with `
  + `${data.statusCode} - ${data.statusText}. `
  + `\n⏱️Took ${data.timeTaken} ms`;

/* Makes human-readable response text for failed check */
const makeErrorMessage = (data) => `❌ Service Unavailable: ${data.hostname || 'Server'} `
  + `resulted in ${data.code || 'a fatal error'} ${data.errno ? `(${data.errno})` : ''}`;

const makeErrorMessage2 = (data) => '❌ Service Error - '
  + `${data.status} - ${data.statusText}`;

/* Kicks of a HTTP request, then formats and renders results */
const makeRequest = (url, options, render) => {
  const {
    headers, enableInsecure, acceptCodes, maxRedirects,
  } = options;
  const validCodes = acceptCodes && acceptCodes !== 'null' ? acceptCodes : null;
  const startTime = new Date();
  const requestMaker = axios.create({
    httpsAgent: new https.Agent({
      rejectUnauthorized: !enableInsecure,
    }),
  });
  requestMaker.request({
    url,
    headers,
    maxRedirects,
  })
    .then((response) => {
      const statusCode = response.status;
      const { statusText } = response;
      const successStatus = getResponseType(statusCode, validCodes);
      const serverName = response.request.socket.servername;
      const timeTaken = (new Date() - startTime);
      const results = {
        statusCode, statusText, serverName, successStatus, timeTaken,
      };
      results.message = makeMessageText(results);
      return results;
    })
    .catch((error) => {
      const response = error ? (error.response || {}) : {};
      const returnCode = response.status || response.code;
      if (validCodes && String(validCodes).includes(returnCode)) { // Success overridden by user
        const results = {
          successStatus: getResponseType(returnCode, validCodes),
          statusCode: returnCode,
          statusText: response.statusText,
          timeTaken: (new Date() - startTime),
        };
        results.message = makeMessageText(results);
        return results;
      } else { // Request failed
        return {
          successStatus: false,
          message: error.response ? makeErrorMessage2(error.response) : makeErrorMessage(error),
        };
      }
    }).then((results) => {
      // Request completed (either successfully, or failed) - render results
      render(JSON.stringify(results));
    });
};

const decodeHeaders = (maybeHeaders) => {
  if (!maybeHeaders) return {};
  const decodedHeaders = decodeURIComponent(maybeHeaders);
  let parsedHeaders = {};
  try {
    parsedHeaders = JSON.parse(decodedHeaders);
  } catch (e) { /* Not valid JSON, will just return false */ }
  return parsedHeaders;
};

/* Returned if the URL param is not present or correct */
const immediateError = (render) => {
  render(JSON.stringify({
    successStatus: false,
    message: '❌ Missing or Malformed URL',
  }));
};

/* Main function, will check if a URL present, and call function */
module.exports = (paramStr, render) => {
  if (!paramStr || !paramStr.includes('=')) {
    immediateError(render);
  } else {
    // Prepare the parameters, which are got from the URL
    const params = new URLSearchParams(paramStr);
    const url = decodeURIComponent(params.get('url'));
    const acceptCodes = decodeURIComponent(params.get('acceptCodes'));
    const maxRedirects = decodeURIComponent(params.get('maxRedirects')) || 0;
    const headers = decodeHeaders(params.get('headers'));
    const enableInsecure = !!params.get('enableInsecure');
    if (!url || url === 'undefined') immediateError(render);
    const options = {
      headers, enableInsecure, acceptCodes, maxRedirects,
    };
    makeRequest(url, options, render);
  }
};
