'use strict';

const colorMap = {
  'turquoise': '#1abc9c',
  'emerald': '#2ecc71',
  'river': '#3498db',
  'amethyst': '#9b59b6',
  'asphalt': '#34495e',
  'sun': '#f1c40f',
  'carrot': '#e67e22',
  'alizarin': '#e74c3c',
  'cloud': '#ecf0f1',
  'concrete': '#95a5a6',
};
const methodColorMap = {
  'info': '#3498db',
  'debug': '#9b59b6',
  'warn': '#e67e22',
  'error': '#e74c3c',
  'log': '#95a5a6',
};
const methodLevel = {
  'debug': 0,
  'log': 1,
  'info': 2,
  'warn': 3,
  'error': 4,
};
class Logger {
  constructor(prefix) {
    this.level = 1;
    this.new = (prefix) => {
      const logger = new Logger(prefix);
      logger.level = this.level;
      logger.color = this.color;
      return logger;
    };
    this.setColor = (color) => {
      this.color = colorMap[color];
      return this;
    };
    this.setLevel = (method) => {
      this.level = methodLevel[method];
      return this;
    };
    this.debug = (...msg) => this.print('debug', ...msg);
    this.debugGroup = (msg, ...groupedMessage) => this.printGroup('debug', msg, ...groupedMessage);
    this.log = (...msg) => this.print('log', ...msg);
    this.logGroup = (msg, ...groupedMessage) => this.printGroup('log', msg, ...groupedMessage);
    this.info = (...msg) => this.print('info', ...msg);
    this.infoGroup = (msg, ...groupedMessage) => this.printGroup('info', msg, ...groupedMessage);
    this.warn = (...msg) => this.print('warn', ...msg);
    this.warnGroup = (msg, ...groupedMessage) => this.printGroup('warn', msg, ...groupedMessage);
    this.error = (...msg) => this.print('error', ...msg);
    this.errorGroup = (msg, ...groupedMessage) => this.printGroup('error', msg, ...groupedMessage);
    this.print = (method, ...msg) => {
      if (methodLevel[method] < this.level)
        return;
      console[method](...this.getColoredMessage(method, msg));
    };
    this.printGroup = (method, msg, ...groupedMessage) => {
      if (methodLevel[method] < this.level)
        return;
      console.groupCollapsed(...this.getColoredMessage(method, msg));
      console.log(...groupedMessage);
      console.groupEnd();
    };
    this.getColoredMessage = (method, ...msg) => {
      return [`%c${method.toUpperCase()} %c${this.prefix} %c${msg.join(' ')}`, `color:${methodColorMap[method]}; font-weight: bold;`, `color:${this.color}; font-weight: bold;`, `color:${'white'}; font-weight: normal;`];
    };
    this.prefix = prefix;
  }
}

class SozlukApi {
  constructor(language = SozlukApi.defaultLanguage) {
    this.logger = new Logger('SozlukClient').setColor('concrete').setLevel('info');
    this.language = language;
    this.contents = [];
  }
  /**
   * Returns the entry with provided key for the specified language. Tries to provide fallback
   * for the fields that are missing in the provided language by adding the fields from the default language.
   * @param key
   * @param defaultData
   */
  async getContent(key, defaultData) {
    const entriesForKey = this.contents.filter(entry => entry.key === key);
    const debugData = {
      language: this.language,
      key,
      defaultData,
      defaultLanguage: SozlukApi.defaultLanguage,
      contents: this.contents,
    };
    if (!entriesForKey) {
      this.logger.warnGroup('A: No entry found.', debugData);
      return undefined;
    }
    const entryInLanguage = entriesForKey.find(entry => entry.language === this.language);
    if (this.language === SozlukApi.defaultLanguage) {
      this.logger.debug('B: Found content which is default language.', Object.assign(Object.assign({}, debugData), { entryInLanguage }));
      // no need to merge the provided language and the default language if they're the same
      return mergeDeep(defaultData, entryInLanguage);
    }
    const entryInDefaultLanguage = entriesForKey.find(entry => entry.language === SozlukApi.defaultLanguage);
    // console.log('entryInDefaultLanguage', entryInDefaultLanguage);
    if (!entryInLanguage && !entryInDefaultLanguage) {
      this.logger.warnGroup('C: No content found.', debugData);
      return defaultData;
    }
    if (!entryInLanguage && entryInDefaultLanguage) {
      this.logger.debug('D: Found content only in default language.', Object.assign(Object.assign({}, debugData), { entryInDefaultLanguage }));
      // return the entry for the default language
      return mergeDeep(defaultData, entryInDefaultLanguage);
    }
    if (entryInLanguage && !entryInDefaultLanguage) {
      this.logger.debug('E: Found content only in provided language.', Object.assign(Object.assign({}, debugData), { entryInLanguage }));
      // return the entry for the provided language without default language fallback
      return mergeDeep(defaultData, entryInLanguage);
    }
    this.logger.debug('F: Found content.', Object.assign(Object.assign({}, debugData), { entryInLanguage,
      entryInDefaultLanguage }));
    // merge the entry for default language on top of the entry for the provided language as fallback
    return mergeDeep(defaultData, mergeDeep(entryInDefaultLanguage, entryInLanguage));
  }
  /**
   * Sets the data locally. getContent() returns the entries from the provided data.
   * @param contents
   */
  setContents(contents) {
    this.contents = contents;
    return this;
  }
  setLanguage(language) {
    this.language = language;
    return this;
  }
}
SozlukApi.defaultLanguage = 'en';
SozlukApi.build = (text, vars) => text.replace(/\{(.*?)}/g, (_, g) => vars[g]);
function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}
function mergeDeep(target = {}, source = {}) {
  let output = Object.assign({}, target);
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target))
          Object.assign(output, { [key]: source[key] });
        else
          output[key] = mergeDeep(target[key], source[key]);
      }
      else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  return output;
}

exports.Logger = Logger;
exports.SozlukApi = SozlukApi;
