/* eslint-disable no-console */

const SUPPORTED_TLDS = ['ru', 'com'];
const PRODLIKE_ENVIRONMENTS = ['production', 'corp', 'prestable', 'corp-prestable'];
const ENVIRONMENT_VARIABLE_PREFIX = 'CONNECT_';

const DEFAULT_LOCALE_MAP = {
    ru: 'ru',
    com: 'en',
};

const TEMPLATED_CONFIG_ENTRIES = [
    'app.assets', 'app.freezePath', 'api.app.default', 'api.app.service',
];

function read(path, description) {
    try {
        return require(path);
    } catch (e) {
        console.error(`Failed to load ${description || path}`, e);
    }
}

const version = read('../package.json').version;
const name = read('../package.json').name;

const _ = require('lodash');
const argv = require('yargs').argv;
const template = require('../lib/template');

const initialConfig = {
    default: {
        // базовый конфиг
        common: {},
        // конфиг окружения
        env: {},
    },
    global: {
        // платформозависимые переменные окружения
        // из `process.env`
        env: {},
        // параметры запуска процесса, приоритетнее переменных окружения
        // из `argv`
        args: {},
    },
    // расширения конфигов под top-level domains
    tld: {
        common: {},
        env: {},
    },
    // локальный конфиг
    local: {},
    // метка текущей версии
    version: {},
    // дополнительные переменные
    runtime: {},
    // финальный экспортируемый конфиг
    final: {},
};

const env = process.env.CONNECT_ENVIRONMENT || process.env.NODE_ENV || 'development';

function pushNormalizedValue(key, value, target) {
    if (!key.startsWith(ENVIRONMENT_VARIABLE_PREFIX)) {
        return;
    }

    // 'CONNECT_APP__LOG_LEVEL' > 'app.logLevel'
    const normalizedKey = key.replace(ENVIRONMENT_VARIABLE_PREFIX, '')
        .split('__')
        .map(component => _.camelCase(component))
        .join('.');

    try {
        value = JSON.parse(value);
    } catch (e) {
        // оставляем строкой как есть
    }

    _.set(target, normalizedKey, value);
}

function revise(config) {
    TEMPLATED_CONFIG_ENTRIES.forEach(key => {
        const entry = _.get(config, key);

        if (entry !== undefined) {
            _.set(config, key, template.build(entry, config));
        }
    });

    // заменяем '~/' в relatedServices на app.root
    if (config.ui && config.ui.relatedServices && config.app.root !== undefined) {
        _.forEach(config.ui.relatedServices, (value, key) => {
            if (/^~\//.test(value)) {
                config.ui.relatedServices[key] = value.replace(/^~\//, config.app.root);
            }
        });
    }

    return config;
}

function update(config, req) {
    const host = req.headers['x-forwarded-host'] || req.headers.host;
    const tld = host.split(':')[0].split('.').pop();

    // домен 'ru' пропускаем, для него достаточно конфига по умолчанию
    if (SUPPORTED_TLDS.indexOf(tld) !== -1 && tld !== 'ru') {
        config.tld.common = read(`./${tld}/common`);
        config.tld.env = read(`./${tld}/${env}`);
    } else {
        config.tld.common = {};
        config.tld.env = {};
    }

    config.runtime.tld = tld;
    config.runtime.defaultLocale = DEFAULT_LOCALE_MAP[tld];
}

function merge(config) {
    return _.merge(
        { setup }, // eslint-disable-line no-use-before-define
        config.default.common,
        config.default.env,
        config.tld.common,
        config.tld.env,
        config.global.env,
        config.global.args,
        { app: config.version },
        { app: config.name },
        { app: config.runtime }
    );
}

function setup(req) {
    let config = _.extend({}, initialConfig);

    config.default.common = read('./default/common');
    config.default.env = read(`./default/${env}`, `${env} environment config`);

    _.forEach(process.env, (value, key) => {
        pushNormalizedValue(key, value, config.global.env);
    });

    _.forEach(argv, (value, key) => {
        pushNormalizedValue(key, value, config.global.args);
    });

    config.version = {
        version,
    };

    config.name = {
        name,
    };

    config.runtime.env = env;
    config.runtime.isProd = PRODLIKE_ENVIRONMENTS.indexOf(env) !== -1;

    if (req) {
        update(config, req);
    }

    config = revise(merge(config));

    return config;
}

const finalConfig = setup();

module.exports = finalConfig;
