/**
 * Upgrade script
 */
const fs = require('fs');
const dotenv = require('dotenv');
const { exit } = require('process');

// Suppress default warnings
const originalConsoleWarn = console.warn;
console.warn = () => {};
const loader = require('./loader');
console.warn = originalConsoleWarn;

// Old Paths
const apiEnvPath = loader.resolve('api/.env');
const clientEnvPath = loader.resolve('client/.env');

// Load into env
dotenv.config({
  path: loader.resolve(apiEnvPath),
});
dotenv.config({
  path: loader.resolve(clientEnvPath),
});
// JS was doing spooky actions at a distance, lets prevent that
const initEnv = JSON.parse(JSON.stringify(process.env));

// New Paths
const rootEnvPath = loader.resolve('.env');
const devEnvPath = loader.resolve('.env.development');
const prodEnvPath = loader.resolve('.env.production');

if (fs.existsSync(rootEnvPath)) {
  console.error('Root env file already exists! Aborting');
  exit(1);
}

// Validate old configs
if (!fs.existsSync(apiEnvPath)) {
  console.error('Api env doesn\'t exit! Did you mean to run install?');
  exit(1);
}
if (!fs.existsSync(clientEnvPath)) {
  console.error('Client env doesn\'t exit! But api/.env does. Manual upgrade required');
  exit(1);
}

/**
 * Refactor the ENV if it has a prod_/dev_ version
 *
 * @param {*} varDev
 * @param {*} varProd
 * @param {*} varName
 */
function refactorPairedEnvVar(varDev, varProd, varName) {
  // Lets validate if either of these are undefined, if so lets use the non-undefined one
  if (initEnv[varDev] === undefined && initEnv[varProd] === undefined) {
    console.error(`Both ${varDev} and ${varProd} are undefined! Manual intervention required!`);
  } else if (initEnv[varDev] === undefined) {
    fs.appendFileSync(rootEnvPath, `\n${varName}=${initEnv[varProd]}`);
  } else if (initEnv[varProd] === undefined) {
    fs.appendFileSync(rootEnvPath, `\n${varName}=${initEnv[varDev]}`);
  } else if (initEnv[varDev] === initEnv[varProd]) {
    fs.appendFileSync(rootEnvPath, `\n${varName}=${initEnv[varDev]}`);
  } else {
    fs.appendFileSync(rootEnvPath, `${varName}=${initEnv[varProd]}\n`);
    fs.appendFileSync(devEnvPath, `${varName}=${initEnv[varDev]}\n`);
  }
}

/**
 * Upgrade the env files!
 * 1. /api/.env will merge into /.env
 * 2. /client/.env will merge into /.env
 * 3. Any prod_/dev_ keys will be split up into .env.development / .env.production files (if they are different)
 */
if (fs.existsSync(apiEnvPath)) {
  fs.copyFileSync(apiEnvPath, rootEnvPath);
  fs.copyFileSync(apiEnvPath, rootEnvPath + '.api.bak');
  fs.unlinkSync(apiEnvPath);
}

// Clean up Domain variables
fs.appendFileSync(
  rootEnvPath,
  '\n\n##########################\n# Domain Variables:\n# Note: DOMAIN_ vars are passed to vite\n##########################\n',
);
refactorPairedEnvVar('CLIENT_URL_DEV', 'CLIENT_URL_PROD', 'DOMAIN_CLIENT');
refactorPairedEnvVar('SERVER_URL_DEV', 'SERVER_URL_PROD', 'DOMAIN_SERVER');

// Remove the old vars
const removeEnvs = {
  NODE_ENV: 'remove',
  OPENAI_KEY: 'remove',
  CLIENT_URL_DEV: 'remove',
  CLIENT_URL_PROD: 'remove',
  SERVER_URL_DEV: 'remove',
  SERVER_URL_PROD: 'remove',
  JWT_SECRET_DEV: 'remove', // Lets regen
  JWT_SECRET_PROD: 'remove', // Lets regen
  VITE_APP_TITLE: 'remove',
  // Comments to remove:
  '#JWT:': 'remove',
  '# Add a secure secret for production if deploying to live domain.': 'remove',
  '# Site URLs:': 'remove',
  '# Don\'t forget to set Node env to development in the Server configuration section above':
    'remove',
  '# if you want to run in dev mode': 'remove',
  '# Change these values to domain if deploying:': 'remove',
  '# Set Node env to development if running in dev mode.': 'remove',
};
loader.writeEnvFile(rootEnvPath, removeEnvs);

/**
 * Lets make things more secure!
 * 1. Add CREDS_KEY
 * 2. Add CREDS_IV
 * 3. Add JWT_SECRET
 */
fs.appendFileSync(
  rootEnvPath,
  '\n\n##########################\n# Secure Keys:\n##########################\n',
);
loader.addSecureEnvVar(rootEnvPath, 'CREDS_KEY', 32);
loader.addSecureEnvVar(rootEnvPath, 'CREDS_IV', 16);
loader.addSecureEnvVar(rootEnvPath, 'JWT_SECRET', 32);

// Lets update the openai key name, not the best spot in the env file but who cares ¯\_(ツ)_/¯
loader.writeEnvFile(rootEnvPath, { OPENAI_API_KEY: initEnv['OPENAI_KEY'] });

// TODO: we need to copy over the value of: APP_TITLE
fs.appendFileSync(
  rootEnvPath,
  '\n\n##########################\n# Frontend Vite Variables:\n##########################\n',
);
const frontend = {
  APP_TITLE: initEnv['VITE_APP_TITLE'] || '"LibreChat"',
  ALLOW_REGISTRATION: 'true',
};
loader.writeEnvFile(rootEnvPath, frontend);

// Ensure .env.development and .env.production files end with a newline
if (fs.existsSync(devEnvPath)) {
  fs.appendFileSync(devEnvPath, '\n');
}
if (fs.existsSync(prodEnvPath)) {
  fs.appendFileSync(prodEnvPath, '\n');
}
// Remove client file
fs.copyFileSync(clientEnvPath, rootEnvPath + '.client.bak');
fs.unlinkSync(clientEnvPath);

console.log('###############################################');
console.log('Upgrade completed! Please review the new .env file and make any changes as needed.');
console.log('###############################################');

// if the .env.development file exists, lets tell the user
if (fs.existsSync(devEnvPath)) {
  console.log(
    'NOTE: A .env.development file was created. This will take precedence over the .env file when running in dev mode.',
  );
  console.log('###############################################');
}
