import { Injectable, EventEmitter } from '@angular/core';

export interface LangChangeEvent {
  lang: string;
}

export interface DefaultLangChangeEvent {
  lang: string;
}

@Injectable({
  providedIn: 'root'
})
export class TranslateService {
  // tslint:disable-next-line: variable-name
  private _changedLang: EventEmitter<LangChangeEvent> = new EventEmitter<
    LangChangeEvent
  >();
  // tslint:disable-next-line: variable-name
  private _changedDefaultLang: EventEmitter<
    DefaultLangChangeEvent
  > = new EventEmitter<DefaultLangChangeEvent>();
  // tslint:disable-next-line: variable-name
  private _defaultLang: string;
  // tslint:disable-next-line: variable-name
  private _currentLang: string;

  /**
   * An EventEmitter to listen to lang change events
   * onLangChange.subscribe((params: LangChangeEvent) => {
   *     // do something
   * });
   */
  get changedLang(): EventEmitter<LangChangeEvent> {
    return this._changedLang;
  }

  /**
   * An EventEmitter to listen to default lang change events
   * onDefaultLangChange.subscribe((params: DefaultLangChangeEvent) => {
   *     // do something
   * });
   */
  get changedDefaultLang() {
    return this._changedDefaultLang;
  }

  /**
   * The default lang to fallback when translations are missing on the current lang
   */
  get defaultLang(): string {
    return this._defaultLang;
  }

  set defaultLang(defaultLang: string) {
    this._defaultLang = defaultLang;
  }

  /**
   * The lang currently used
   */
  get currentLang(): string {
    return this._currentLang;
  }

  set currentLang(currentLang: string) {
    this._currentLang = currentLang;
  }

  constructor() {}

  /**
   * Sets the default language to use as a fallback
   */
  public setDefaultLang(lang: string): void {
    if (lang === this.defaultLang) {
      return;
    }

    this.changeDefaultLang(lang);
  }

  /**
   * Gets the default language used
   */
  public getDefaultLang(): string {
    return this.defaultLang;
  }

  /**
   * Changes the lang currently used
   */
  public use(lang: string): void {
    // don't change the language if the language given is already selected
    if (lang === this.currentLang) {
      return;
    }

    this.changeLang(lang);
  }

  /**
   * Gets the translated value of a key (or an array of keys)
   * @returns the translated key, or an object of translated keys
   */
  public get(target: any, key: string, separator?: string): string {
    if (!target || 0 === Object.keys(target).length) {
      throw new Error(`Parameter "target" is not valid`);
    }
    if (!key || 0 === key.length) {
      throw new Error(`Parameter "key" required`);
    }

    if (target instanceof Array) {
      const result: string[] = [];
      target.forEach(t => {
        result.push(this.get(t, key, separator));
      });
      return result.join(!!separator ? separator : '');
    }

    const keys = Object.keys(target);
    try {
      const lang =
        this.currentLang.charAt(0).toUpperCase() +
        this.currentLang.substring(1);
      const langKey = `${key}${lang}`;

      if (-1 !== keys.indexOf(langKey)) {
        return target[langKey];
      }
    } catch (error) {}

    if (-1 !== keys.indexOf(key)) {
      return target[key];
    }

    throw new Error(`value is not exist`);
  }

  /**
   * Changes the current lang
   */
  private changeLang(lang: string): void {
    this.currentLang = lang;
    this.changedLang.emit({ lang });

    // if there is no default lang, use the one that we just set
    if (!this.defaultLang) {
      this.changeDefaultLang(lang);
    }
  }

  /**
   * Changes the default lang
   */
  private changeDefaultLang(lang: string): void {
    this.defaultLang = lang;
    this.changedDefaultLang.emit({ lang });
  }
}
