/*
 * Copyright (C) Sapphirecode - All Rights Reserved
 * This file is part of console-app which is released under MIT.
 * See file 'LICENSE' for full license details.
 * Created by Timo Hocker <timo@scode.ovh>, May 2020
 */

/* eslint-disable no-await-in-loop */
import { dirname, join } from 'path';
import fs from 'fs-extra';
import { run_regex } from '@sapphirecode/utilities';
import hjson from 'hjson';
import { OptionValue, Option } from '../Option';
import { ErrorCallback } from '../ErrorCallback';
import { OptionSource } from './OptionSource';

export class ConfigSource extends OptionSource {
  private _config_files: string[];

  public constructor (config_files: string[], error_callback?:ErrorCallback) {
    super (error_callback);
    this._config_files = config_files;
  }

  private async read_json_file (file: string):
   Promise<Record<string, unknown>> {
    if (!await fs.pathExists (file))
      return {};
    const dir = dirname (file);
    const contents = await fs.readFile (file, 'utf-8');
    const obj: Record<string, unknown> = {};
    const regex = /^#include (?<f>.+)/gmui;
    const includes: string[] = [];
    run_regex (regex, contents, (res: {groups:{f:string}}) => {
      includes.push (join (dir, res.groups.f));
    });
    for (const inc of includes) {
      const data = await this.read_json_file (inc);
      for (const key of Object.keys (data))
        obj[key] = data[key];
    }
    const config = hjson.parse (contents);
    for (const key of Object.keys (config))
      obj[key] = config[key];

    return obj;
  }

  public async parse (opt: Option, val: OptionValue): Promise<void> {
    const data: Record<string, unknown> = {};
    for (const f of this._config_files) {
      try {
        const json = await this.read_json_file (f);
        for (const key of Object.keys (json))
          data[key] = json[key];
      }
      catch (e) {
        if (typeof this.error_callback !== 'undefined')
          this.error_callback ('*', `config file: ${f}`, e);
        continue;
      }
    }

    const keys = Object.keys (data);

    if (keys.includes (opt.name))
      await val.assign_arg (opt, data[opt.name]);
  }
}