const fetch = require('node-fetch');
const fs = require('fs-extra');
const YAML = require('yaml');
const chalk = require('chalk');
const { contentProceduresPath } = require('../const');
const { pipe, join, dropLast } = require('ramda');

const printHelp = (exit) => {
  console.log(`
    Example usage:
    studio-report --procedure={PROCEDURE_NAME}

    --help                                 Print this help
    --dev                                  POST payload to a local server at localhost
    --log                                  Save {PROCEDURE_NAME}.json and payload to disk
    --endpoint={ENDPOINT}                  Where the payload will be sent
    --output={OUTPUT_PATH}                 Where the generated document or assets will be saved
    --extract                              Copy all procedure assets (images, videos)
                                           to the default or specified output folder,
                                           and the generate html report
    --procedure={PROCEDURE_NAME}           Procedure to process, must correspond
                                           to a procedure folder name in
                                           ~/git/content-procedures/
    --input_json={PATH_TO_FILE}            Path to the json file containing procedure assets.
                                           File can be generated by running the script first with --log
    --language                             Language iso string for generating localised documents
    --validate_translations                Print procedure text which is not present in translations
  `);
  exit && process.exit(0);
};

const error = (msg, includeHelp = false, isFatal = true) => {
  console.error(chalk`{inverse.red [ERROR]} {red ${msg}}`);
  if (includeHelp) {
    printHelp();
  }
  if (isFatal) {
    process.exit(1);
  }
};

const mkdirIfNotExists = (path) => {
  if (!fs.existsSync(path)) {
    fs.mkdirSync(path, { recursive: true });
  }
};

const writeJSON = (path, data) => {
  const folderPath = pipe(dropLast(1), join('/'))(path.split('/'));
  mkdirIfNotExists(folderPath);
  return fs.writeJson(path, data, { spaces: 2 }, (err) => {
    if (err) {
      error(err);
    }
  });
};

const downloadDocument = async (data, endpoint, outputPath) => {
  const folderPath = pipe(dropLast(1), join('/'))(outputPath.split('/'));
  mkdirIfNotExists(folderPath);
  return await fetch(`${endpoint}`, {
    method: 'post',
    body: data,
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(JSON.stringify(data)),
      Connection: 'keep-alive',
      'Access-Control-Allow-Origin': '*',
      Accept: '*/*',
      'Accept-Encoding': 'gzip, deflate, br',
    },
    maxContentLength: Infinity,
    maxBodyLength: Infinity,
  })
    .then(
      (response) =>
        new Promise((resolve, reject) => {
          if (response.status === 200) {
            const writer = fs.createWriteStream(outputPath);
            response.body.pipe(writer);
            let error = null;
            writer.on('error', (err) => {
              error = err;
              writer.close();
              reject(err);
            });
            writer.on('close', () => {
              if (!error) {
                resolve(true);
              }
              console.log(`Document saved at: ${outputPath}`);
            });
          }
          if (response.status === 422) {
            const errorJSONoutputPath = `${outputPath.slice(
              0,
              -4
            )}_validation_errors.json`;
            const writer = fs.createWriteStream(errorJSONoutputPath);
            response.body.pipe(writer);
            let error = null;
            writer.on('error', (err) => {
              error = err;
              writer.close();
              reject(err);
            });
            writer.on('close', () => {
              if (!error) {
                resolve(true);
              }
              console.log(
                `Payload did not pass validation, error log saved at: ${errorJSONoutputPath}`
              );
            });
          }
        })
    )
    .catch((err) => {
      error(`Connecting to pdf service failed. \n${err}`);
    });
};

const generateYAMLJSON = (procedureName) => {
  let procedure;
  try {
    procedure = YAML.parse(
      fs.readFileSync(
        `${contentProceduresPath}/${procedureName}/procedure.yml`,
        'utf-8'
      )
    );
  } catch (err) {
    error(err);
  }
  return Object.assign({}, procedure, {
    procedure_name: procedureName,
    phases: procedure.phases.map((phase) =>
      Object.assign(
        {},
        YAML.parse(
          fs.readFileSync(
            `${contentProceduresPath}/${procedureName}/${phase}/phase.yml`,
            'utf-8'
          )
        ),
        { folder_name: phase }
      )
    ),
  });
};

const copyProcedureAssets = (procedureName, outputFolder) => {
  let procedure;
  try {
    procedure = YAML.parse(
      fs.readFileSync(
        `${contentProceduresPath}/${procedureName}/procedure.yml`,
        'utf-8'
      )
    );
  } catch (err) {
    error(err);
  }
  mkdirIfNotExists(outputFolder);

  if (fs.existsSync(`${contentProceduresPath}/${procedureName}/assets`)) {
    mkdirIfNotExists(`${outputFolder}/assets`);
    fs.copy(
      `${contentProceduresPath}/${procedureName}/assets`,
      `${outputFolder}/assets`,
      (err) => {
        if (err) {
          console.log(err);
          console.log(`Error while copying ${procedureName} assets`);
        }
      }
    );
  }

  procedure.phases.forEach((phase) => {
    if (
      fs.existsSync(`${contentProceduresPath}/${procedureName}/${phase}/assets`)
    ) {
      mkdirIfNotExists(`${outputFolder}/${phase}/assets`);
      fs.copy(
        `${contentProceduresPath}/${procedureName}/${phase}/assets`,
        `${outputFolder}/${phase}/assets`,
        (err) => {
          if (err) {
            console.log(err);
            console.log(`Error while copying ${procedureName} ${phase} assets`);
          }
        }
      );
    }
  });
};

const getSha = () => {
  return require('child_process')
    .execSync(`git -C ${contentProceduresPath} rev-parse --short HEAD`)
    .toString()
    .trim();
};

module.exports = {
  printHelp,
  error,
  writeJSON,
  downloadDocument,
  generateYAMLJSON,
  copyProcedureAssets,
  getSha
};
