/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: BUSL-1.1
 */

/*eslint ember/no-jquery: "off", ember/no-global-jquery: "off"*/
'use strict';
const path = require('path');
const exists = require('fs').existsSync;

const Funnel = require('broccoli-funnel');
const mergeTrees = require('broccoli-merge-trees');
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const utils = require('./config/utils');

// const BroccoliDebug = require('broccoli-debug');
// const debug = BroccoliDebug.buildDebugCallback(`app:consul-ui`)

module.exports = function (defaults, $ = process.env) {
  // available environments
  // ['production', 'development', 'staging', 'test'];

  $ = utils.env($);
  const env = EmberApp.env();
  const isProd = ['production'].includes(env);
  const prodlike = ['production', 'staging'];
  const devlike = ['development', 'staging'];
  const sourcemaps = !['production'].includes(env) && !$('BABEL_DISABLE_SOURCEMAPS', false);

  const trees = {};
  const addons = {};
  const outputPaths = {};
  let excludeFiles = [];

  const apps = [
    'consul-ui',
    'consul-acls',
    'consul-lock-sessions',
    'consul-peerings',
    'consul-partitions',
    'consul-nspaces',
  ].map((item) => {
    return {
      name: item,
      path: path.dirname(require.resolve(`${item}/package.json`)),
    };
  });

  const babel = {
    plugins: ['@babel/plugin-proposal-object-rest-spread'],
    sourceMaps: sourcemaps ? 'inline' : false,
  };

  // setup up different build configuration depending on environment
  if (!['test'].includes(env)) {
    // exclude any component/pageobject.js files from anything but test
    excludeFiles = excludeFiles.concat([
      'components/**/pageobject.js',
      'components/**/test-support.js',
      'components/**/*.test-support.js',
      'components/**/*.test.js',
    ]);
  }

  if (['test', 'production'].includes(env)) {
    // exclude our debug initializer, route and template
    excludeFiles = excludeFiles.concat([
      'instance-initializers/debug.js',
      'routing/**/*-debug.js',
      'helpers/**/*-debug.js',
      'modifiers/**/*-debug.js',
      'services/**/*-debug.js',
      'templates/debug.hbs',
      'components/debug/**/*.*',
    ]);
    // inspect *-debug configuration files for files to exclude
    excludeFiles = apps.reduce((prev, item) => {
      return ['services', 'routes'].reduce((prev, type) => {
        const path = `${item.path}/vendor/${item.name}/${type}-debug.js`;
        if (exists(path)) {
          return Object.entries(JSON.parse(require(path)[type])).reduce(
            (prev, [key, definition]) => {
              if (typeof definition.class !== 'undefined') {
                return prev.concat(`${definition.class.replace(`${item.name}/`, '')}.js`);
              }
              return prev;
            },
            prev
          );
        }
        return prev;
      }, prev);
    }, excludeFiles);
    // exclude any debug like addons from production or test environments
    addons.blacklist = [
      // exclude docfy
      '@docfy/ember',
    ];
  }

  if (['production'].includes(env)) {
    // everything apart from production is 'debug', including test
    // which means this and everything it affects is never tested
    babel.plugins.push(['strip-function-call', { strip: ['Ember.runInDebug'] }]);
  }

  //
  (function (apps) {
    trees.app = mergeTrees(
      [new Funnel('app', { exclude: excludeFiles })].concat(
        apps
          .filter((item) => exists(`${item.path}/app`))
          .map((item) => new Funnel(`${item.path}/app`, { exclude: excludeFiles }))
      ),
      {
        overwrite: true,
      }
    );
    // we switched to postcss - because ember-cli-postcss only operates on the
    // styles tree we need to make sure we write the css files from "sub-apps"
    // into `app/styles` manually and prefix them with `consul-ui` because that
    // is what the codebase expects from before when using ember-cli-sass.
    trees.styles = mergeTrees(
      [
        new Funnel('app/styles', { include: ['**/*.{scss,css}'] }),
        new Funnel('app', { include: ['components/**/*.{scss,css}'], destDir: 'consul-ui' }),
      ].concat(
        apps
          .filter((item) => exists(`${item.path}/app`))
          .map(
            (item) =>
              new Funnel(`${item.path}/app`, {
                include: ['**/*.{scss,css}'],
                destDir: 'consul-ui',
              })
          )
      ),
      {
        overwrite: true,
      }
    );
    trees.vendor = mergeTrees(
      [new Funnel('vendor')].concat(apps.map((item) => new Funnel(`${item.path}/vendor`)))
    );
  })(
    // consul-ui will eventually be a separate app just like the others
    // at which point we can remove this filter/extra scope
    apps.filter((item) => item.name !== 'consul-ui')
  );
  //

  let app = new EmberApp(
    Object.assign({}, defaults, {
      productionEnvironments: prodlike,
    }),
    {
      trees: trees,
      addons: addons,
      outputPaths: outputPaths,
      'ember-cli-babel': {
        includePolyfill: true,
      },
      postcssOptions: {
        compile: {
          extension: 'scss',
          plugins: [
            {
              module: require('@csstools/postcss-sass'),
              options: {
                includePaths: [
                  '../../node_modules/@hashicorp/design-system-tokens/dist/products/css',
                ],
              },
            },
            {
              module: require('tailwindcss'),
              options: {
                config: './tailwind.config.js',
              },
            },
            {
              module: require('autoprefixer'),
            },
          ],
        },
      },
      'ember-cli-string-helpers': {
        only: [
          'capitalize',
          'lowercase',
          'truncate',
          'uppercase',
          'humanize',
          'titleize',
          'classify',
        ],
      },
      'ember-cli-math-helpers': {
        only: ['div'],
      },
      babel: babel,
      autoImport: {
        // allows use of a CSP without 'unsafe-eval' directive
        forbidEval: true,
        publicAssetURL: isProd ? '{{.ContentPath}}assets' : undefined,
      },
      codemirror: {
        keyMaps: ['sublime'],
        addonFiles: [
          'lint/lint.css',
          'lint/lint.js',
          'lint/json-lint.js',
          'lint/yaml-lint.js',
          'mode/loadmode.js',
        ],
      },
      sassOptions: {
        implementation: require('sass'),
        sourceMapEmbed: sourcemaps,
      },
    }
  );
  const build = function (path, options) {
    const { root, ...rest } = options;
    if (exists(`${root}/${path}`)) {
      app.import(path, rest);
    }
  };
  apps.forEach((item) => {
    build(`vendor/${item.name}/routes.js`, {
      root: item.path,
      outputFile: `assets/${item.name}/routes.js`,
    });
    build(`vendor/${item.name}/services.js`, {
      root: item.path,
      outputFile: `assets/${item.name}/services.js`,
    });
    if (devlike) {
      build(`vendor/${item.name}/routes-debug.js`, {
        root: item.path,
        outputFile: `assets/${item.name}/routes-debug.js`,
      });
      build(`vendor/${item.name}/services-debug.js`, {
        root: item.path,
        outputFile: `assets/${item.name}/services-debug.js`,
      });
    }
  });
  // Use `app.import` to add additional libraries to the generated
  // output files.
  //
  // If you need to use different assets in different
  // environments, specify an object as the first parameter. That
  // object's keys should be the environment name and the values
  // should be the asset to use in that environment.
  //
  // If the library that you are including contains AMD or ES6
  // modules that you would like to import into your application
  // please specify an object with the list of modules as keys
  // along with the exports of each module as its value.

  // TextEncoder/Decoder polyfill. See assets/index.html
  app.import('node_modules/text-encoding/lib/encoding.js', {
    outputFile: 'assets/encoding.js',
  });
  app.import('node_modules/text-encoding/lib/encoding-indexes.js', {
    outputFile: 'assets/encoding-indexes.js',
  });

  // CSS.escape polyfill
  app.import('node_modules/css.escape/css.escape.js', { outputFile: 'assets/css.escape.js' });

  // JSON linting support. Possibly dynamically loaded via CodeMirror linting. See components/code-editor.js
  app.import('node_modules/jsonlint/lib/jsonlint.js', {
    outputFile: 'assets/codemirror/mode/javascript/javascript.js',
  });
  app.import('node_modules/codemirror/mode/javascript/javascript.js', {
    outputFile: 'assets/codemirror/mode/javascript/javascript.js',
  });

  // HCL/Ruby linting support. Possibly dynamically loaded via CodeMirror linting. See components/code-editor.js
  app.import('node_modules/codemirror/mode/ruby/ruby.js', {
    outputFile: 'assets/codemirror/mode/ruby/ruby.js',
  });

  // YAML linting support. Possibly dynamically loaded via CodeMirror linting. See components/code-editor.js
  app.import('node_modules/js-yaml/dist/js-yaml.js', {
    outputFile: 'assets/codemirror/mode/yaml/yaml.js',
  });
  app.import('node_modules/codemirror/mode/yaml/yaml.js', {
    outputFile: 'assets/codemirror/mode/yaml/yaml.js',
  });
  // XML linting support. Possibly dynamically loaded via CodeMirror linting. See services/code-mirror/linter.js
  app.import('node_modules/codemirror/mode/xml/xml.js', {
    outputFile: 'assets/codemirror/mode/xml/xml.js',
  });
  // metrics-providers
  app.import('vendor/metrics-providers/consul.js', {
    outputFile: 'assets/metrics-providers/consul.js',
  });
  app.import('vendor/metrics-providers/prometheus.js', {
    outputFile: 'assets/metrics-providers/prometheus.js',
  });
  app.import('vendor/init.js', {
    outputFile: 'assets/init.js',
  });
  return app.toTree();
};
