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

/* eslint-disable no-await-in-loop */
import path from 'path';
import fs from 'fs-extra';
import { Confirm, Input, AutoComplete } from 'enquirer';
// eslint-disable-next-line id-match
import { findLicense, getLicense } from 'license';
import { Snippet } from '../../Snippet';
import { DialogHandler } from '../../dialog';
import { modify_json } from '../../Helper';
import { CopyrightGenerator } from './copyright_generator';
import { FileMapper } from './file_mapper';
import { CopyrightOptions } from './copyright_options';

export default class Copyright implements Snippet {
  public is_active (): boolean {
    return true;
  }

  private _options: CopyrightOptions | null = null;
  private _cwd = '';
  private _loaded_from_config = false;

  async start (cwd: string): Promise<void> {
    this._cwd = cwd;
    await this.load_options_file ();
    if (!this._options)
      await this.gather_options ();

    const options = this._options as CopyrightOptions;

    await FileMapper.map_all_files (
      this._cwd,
      this.fix_file_license.bind (this)
    );

    await modify_json ((json) => {
      json.author = { name: options.author, email: options.email };
      json.license = options.has_license ? options.license : 'UNLICENSED';
      return json;
    });

    if (options.has_license) {
      await fs.writeFile (
        path.join (cwd, 'LICENSE'),
        getLicense (options.license, {
          name:    options.author,
          email:   options.email,
          project: options.software
        })
      );
    }

    if (
      !this._loaded_from_config
      && (await new Confirm (
        { message: 'should those settings be saved for the next run?' }
      )
        .run ()
        .catch (DialogHandler.catch))
    )
      this.save_options_file ();
  }

  private async gather_options (): Promise<void> {
    this._options = (new CopyrightOptions);
    this._options.author = await new Input ({ message: 'author' })
      .run ()
      .catch (DialogHandler.catch);
    this._options.email = await new Input ({ message: 'email' })
      .run ()
      .catch (DialogHandler.catch);
    this._options.company = await new Input ({ message: 'company' })
      .run ()
      .catch (DialogHandler.catch);
    this._options.software = await new Input ({ message: 'software name' })
      .run ()
      .catch (DialogHandler.catch);
    this._options.has_license = await new Confirm (
      { message: 'would you like to specify a license?' }
    )
      .run ()
      .catch (DialogHandler.catch);
    if (this._options.has_license) {
      this._options.license = await new AutoComplete ({
        name:    'license',
        message: 'choose a license',
        limit:   10,
        choices: findLicense ('')
      })
        .run ()
        .catch (DialogHandler.catch);
    }
  }

  private async load_options_file (): Promise<void> {
    const file_path = path.join (this._cwd, '.liconfig.json');
    this._options = null;

    if (await fs.pathExists (file_path)) {
      const options = JSON.parse (await fs.readFile (file_path, 'utf-8'));

      // eslint-disable-next-line no-console
      console.log (`author: ${options.author}
email: ${options.email}
company: ${options.company}
software name: ${options.software}
license: ${options.license}`);
      const should_load = await new Confirm (
        { message: 'should those options be used?' }
      )
        .run ()
        .catch (DialogHandler.catch);
      if (should_load) {
        this._options = options;
        this._loaded_from_config = true;
      }
    }
  }

  private async save_options_file (): Promise<void> {
    const file_path = path.join (this._cwd, '.liconfig.json');
    await fs.writeFile (
      file_path,
      JSON.stringify (this._options, null, 2),
      'utf-8'
    );
  }

  private fix_file_license (data: string, filename: string): string | null {
    // eslint-disable-next-line max-len
    const regex = /\/\*\s+\*\sCopyright[\s\S]*?(?:Created by.*?, (?<date>[a-z]+ [0-9]+)[\s\S]*?|\s)\*\/\n{0,2}/gui;
    const shebang = /^#!.*?\n\n/gu;
    const shebang_line = shebang.exec (data);

    if (!(/\.(?:js|ts|mjs)$/u).test (filename) && !regex.test (data))
      return null;
    const res = regex.exec (data);
    return (
      (shebang_line ? shebang_line[0] : '')
      + CopyrightGenerator.get_copyright_notice (
        this._options as CopyrightOptions,
        res?.groups?.date
      )
      + data.replace (regex, '')
        .replace (shebang, '')
    );
  }
}