/**
 * Metrics for components cli
 * Author: Meng Zong <meng.zong@serverless.com>
 * Doc: https://hackmd.io/_xwPrkdET2aKKr1VWcaWjQ?view
 */

'use strict';
const path = require('path');
const ci = require('ci-info');
const { writeClientUid } = require('../utils');

const CHECKED_COMPONENTS = ['scf', 'multi-scf'];

/*
 * This functions is used for collecting event_types and event_count for scf and multi-scf components
 */
const resolveEvents = (component, inputs) => {
  if (!CHECKED_COMPONENTS.includes(component) || !inputs) {
    return null;
  }

  if (component === 'scf') {
    if (!inputs.events || inputs.events.length === 0) {
      return null;
    }

    const types = inputs.events.map((item) => Object.keys(item)[0]);

    return [types.length, types];
  } else if (component === 'multi-scf') {
    if (!inputs.triggers || inputs.triggers.length === 0) {
      return null;
    }

    const types = inputs.triggers.reduce((total, curr) => total.push(curr.type), []);
    return [types.length, types];
  }

  return null;
};

// Collect project's npm dependencies, due to this's an expensive action, we only run it when rootConfig has a **depMode** field as true
// And it will not collect sub-instance's dependencies within a template project, it will make action very slow
const npmDependencies = (p) => {
  const pkgJson = (() => {
    try {
      return require(path.resolve(p, 'package.json'));
    } catch (error) {
      return null;
    }
  })();
  if (!pkgJson) return [];
  return Array.from(
    new Set([
      ...Object.keys(pkgJson.dependencies || {}),
      ...Object.keys(pkgJson.optionalDependencies || {}),
      ...Object.keys(pkgJson.devDependencies || {}),
    ])
  );
};

/*
 * @param{string}command: the using command
 * @param{string}userId: If this is a auth command, we can get the userId(appid) for the current user
 * @param{object}rootConfig: the root yaml config in project, if project is component, it should also be instance config, or it's a template root config
 * @param{[]ojbect}configs: if this project is a template, configs contains all sub-instance's yaml config
 * @@param{string}serviceDir: It should always be the location command running
 */
module.exports = async ({
  command,
  userId = null,
  rootConfig = null,
  configs = null,
  serviceDir = process.cwd(),
}) => {
  try {
    const { value: clientUid } = await writeClientUid(); // get client uid, if it doesn't exist, create one firstly.

    if (!command) {
      throw new Error('command is required for sending metrics analytics');
    }

    const ciName = (() => {
      if (process.env.SERVERLESS_CI_CD) {
        return 'Serverless CI/CD';
      }

      if (process.env.SEED_APP_NAME) {
        return 'Seed';
      }

      if (ci.isCI) {
        if (ci.name) {
          return ci.name;
        }
        return 'unknown';
      }
      return null;
    })();

    let payload = {
      event: `components.command.${command}`,
      client_uid: clientUid,
      timestamp: Date.now(),
      provider_name: 'tencent', // This should be fixed as 'tencent'
      ciName,
      outcome: 'success', // default outcome is success
    };

    // If this is an action(deploy, logs...) which needs auth, it can have a userID(appId), we save and send it, or skip it(init, invoke local...)
    if (userId) {
      payload.user_uid = userId;
    }

    try {
      payload.timezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
      payload.components_cli_version = require('../../../../package.json').version;
      // eslint-disable-next-line no-empty
    } catch (e) {}

    const components = []; // Which components used in this project
    const providerRuntimes = []; // which runtime used in this project

    if (rootConfig) {
      if (rootConfig.depMode) {
        payload.npmDependencies = npmDependencies(serviceDir);
      }
      if (rootConfig.stage) {
        payload.provider_stage = rootConfig.stage;
      }

      if (rootConfig.component) {
        components.push(rootConfig.component);

        if (rootConfig.inputs) {
          // Tencent supports two types for component: web and event
          if (rootConfig.inputs.type) {
            payload.type = rootConfig.inputs.type;
          }
          if (rootConfig.inputs.runtime) {
            providerRuntimes.push(rootConfig.inputs.runtime);
          }
          if (rootConfig.inputs.region) {
            payload.provider_region = rootConfig.inputs.region;
          }
        }
        // count single instance project's functions number, only works for scf and multi-scf components
        if (rootConfig.component === 'scf') {
          payload.functions_count = 1;
        } else if (
          rootConfig.component === 'multi-scf' &&
          rootConfig.inputs &&
          rootConfig.inputs.functions
        ) {
          payload.functions_count = Object.keys(rootConfig.inputs.functions).length;
        }

        const resolveEventsResult = resolveEvents(rootConfig.component, rootConfig.inputs);
        if (resolveEventsResult) {
          payload.events_count = resolveEventsResult[0];
          payload.events_type = resolveEventsResult[1];
        }
      }
    }

    // collection sub-instance's info
    if (configs) {
      for (const config of configs) {
        if (config.component) {
          components.push(config.component);
        }

        // If we already set stage from rootConfig, we do not need to set it again, because all sub-instance will inherit that value
        if (!payload.provider_stage && config.stage) {
          payload.provider_stage = config.stage;
        }

        if (config.inputs) {
          if (config.inputs.runtime) {
            providerRuntimes.push(config.inputs.runtime);
          }
          if (config.inputs.region) {
            payload.provider_region = config.inputs.region;
          }
        }

        // collect functions count for sub-instances
        if (config.component === 'scf') {
          if (payload.functions_count) {
            payload.functions_count += 1;
          } else {
            payload.functions_count = 1;
          }
        } else if (config.component === 'multi-scf' && config.inputs && config.inputs.functions) {
          const count = Object.keys(config.inputs.functions);
          if (payload.functions_count) {
            payload.functions_count += count;
          } else {
            payload.functions_count = count;
          }
        }

        // collect events for each sub-instance
        const resolveEventsResult = resolveEvents(rootConfig.component, rootConfig.inputs);
        if (resolveEventsResult) {
          if (payload.events_count) {
            payload.events_count += resolveEventsResult[0];
            payload.events_type += resolveEventsResult[1];
          } else {
            payload.events_count = resolveEventsResult[0];
            payload.events_type = resolveEventsResult[1];
          }
        }
      }
    }

    payload = {
      ...payload,
      components,
      provider_runtimes: providerRuntimes,
    };

    return payload;
  } catch (e) {
    return {};
  }
};
