'use strict';

/*
 * Serverless Components: Utilities
 */

const {
  readConfigFile,
  writeConfigFile,
  refreshToken,
  listTenants,
} = require('@serverless/platform-sdk');
const path = require('path');
const fse = require('fs-extra');
const dotenv = require('dotenv');

const sleep = async (wait) => new Promise((resolve) => setTimeout(() => resolve(), wait));

/**
 * Checks if a file exists
 * @param {*} filePath
 */
const fileExists = async (filePath) => {
  try {
    const stats = await fse.lstat(filePath);
    return stats.isFile() || stats.isSymbolicLink();
  } catch (error) {
    return false;
  }
};

/**
 * Get default org name by fetching all Orgs and picking the first one which the user is the owner of
 */
const getDefaultOrgName = async () => {
  const res = readConfigFile();

  if (!res.userId || !res.users || !res.users[res.userId] || !res.users[res.userId].dashboard) {
    return null;
  }

  let { defaultOrgName } = res.users[res.userId].dashboard;

  // if defaultOrgName is not in RC file, fetch it from the platform
  if (!defaultOrgName) {
    await refreshToken();

    const userConfigFile = readConfigFile();

    const { username, dashboard } = userConfigFile.users[userConfigFile.userId];
    const { idToken } = dashboard;
    const orgsList = await listTenants({ username, idToken });

    // filter by owner
    const filteredOrgsList = orgsList.filter((org) => org.role === 'owner');

    defaultOrgName = filteredOrgsList[0].orgName;

    res.users[res.userId].dashboard.defaultOrgName = defaultOrgName;

    writeConfigFile(res);
  }

  return defaultOrgName;
};

/**
 * Check whether the user is logged in
 */
const isLoggedIn = () => {
  const userConfigFile = readConfigFile();
  // If userId is null, they are not logged in.  They also might be a new user.
  if (!userConfigFile.userId) {
    return false;
  }
  if (!userConfigFile.users[userConfigFile.userId]) {
    return false;
  }
  return true;
};

/**
 * Gets the logged in user's token id, or access key if its in env
 */
const getAccessKey = async () => {
  // if access key in env, use that for CI/CD
  if (process.env.SERVERLESS_ACCESS_KEY) {
    return process.env.SERVERLESS_ACCESS_KEY;
  }

  if (!isLoggedIn()) {
    return null;
  }

  // refresh token if it's expired.
  // this platform-sdk method returns immediately if the idToken did not expire
  // if it did expire, it'll refresh it and update the config file
  await refreshToken();

  // read config file from user machine
  const userConfigFile = readConfigFile();

  // Verify config file and that the user is logged in
  if (!userConfigFile || !userConfigFile.users || !userConfigFile.users[userConfigFile.userId]) {
    return null;
  }

  const user = userConfigFile.users[userConfigFile.userId];

  // why are we using access keys in the CLI instead of idToken?
  // if (user.dashboard.accessKeys && user.dashboard.accessKeys[org]) {
  //   return user.dashboard.accessKeys[org];
  // }
  return user.dashboard.idToken;
};

const getLoginBrokerUrl = () => {
  if (process.env.SERVERLESS_PLATFORM_STAGE === 'dev') {
    return 'https://api.serverless-dev.com/login/';
  }

  return 'https://api.serverless.com/login/';
};

const getEnvVars = () => {
  const envFilePath = path.join(getRoot(), '.env');
  const { parsed } = dotenv.config({ path: envFilePath });

  return parsed;
};

const getRoot = () => process.cwd();

const isProject = () => {
  try {
    const app = require(getRoot());

    if (!app || typeof app !== 'function' || typeof app.use !== 'function') {
      return false;
    }

    return true;
  } catch (e) {
    if (
      e.message.includes("Cannot find module 'express'") ||
      e.message.includes("Cannot find module 'koa'") ||
      e.message.includes("Cannot find module '@serverless/data-internal'") ||
      e.message.includes("Cannot find module '@serverless/data'")
    ) {
      return true;
    }

    throw e;
  }
};

const getPackageJson = () => {
  try {
    return require(path.join(getRoot(), 'package.json'));
  } catch (e) {
    return {};
  }
};

const getProjectConfig = async () => {
  const packageJson = getPackageJson();

  const config = {
    name: 'demo', // todo change
    service: 'demo', // todo change
    org: packageJson.org || (await getDefaultOrgName()),
    src: getRoot(),
    env: getEnvVars(),
  };

  config.accessKey = await getAccessKey(config.org);

  return config;
};

const isCwdEmpty = () => {
  return fse.promises.readdir(process.cwd()).then((files) => {
    return files.length === 0;
  });
};

const readDataFile = () => {
  let data;
  try {
    data = require(path.join(getRoot(), 'data.json'));
  } catch (e) {
    data = {};
  }

  return data;
};

const generateTemplate = async () => {
  await fse.copy(path.join(__dirname, 'templates', 'simple'), getRoot());
};

module.exports = {
  sleep, // utils
  getAccessKey, // user
  isLoggedIn, // user
  getDefaultOrgName, // user
  getLoginBrokerUrl, // user
  getRoot, // FS getProjectRoot
  getProjectConfig, // FS readProjectFile
  isProject, // FS isProjectDirectory
  isCwdEmpty, // FS isCwdEmpty
  readDataFile, // FS readDataFile
  generateTemplate, // FS
  getEnvVars, // FS
};

/*

Cloud - For interacting with the Cloud API
FS - Interface for the FS

User - For managing user login, logout & config
Org - For managing org access, and listing services
Service - For managing services & listing instances
Instance - For managing instances
Code - For managing code uploads & zipping

|- models
  |- Cloud
  |- User
  |- Org
  |- Service
  |- Instance
  |- Code
  |- Data
  |- CLI
  |- FS
|- commands
  |- deploy.js
  |- dev.js
  |- seed.js
  |- login.js
  |- logout.js
|- templates
|- index.js



*/
