'use strict';

// GLOBAL
const path = require('path');
const execa = require('execa');

// LOCAL
const tools = require('../../utils/tools');
const gitUtils = require('../../utils/git-utils');

// FETCH ARGS
const { dryRun, skipCommitChecks } = tools.getArgs();


const getMetadata = (pkg, opts) => {

  let buildMetadata = {};

  return Promise.resolve()
    .then(() => {
      tools.logTitle('Extracting commits...');
      return extractCommits(pkg, opts);
    })
    .then((commits) => {
      buildMetadata.commits = commits;

      if (commits.length !== 0) {
        tools.logSuccess();
      } else {
        tools.logInfo('No valid commit found');
      }

      return commits;
    })

    .then((commits) => {
      if (commits.length !== 0) {
        tools.logTitle('Extracting issues...');
        return extractIssuesFromCommits(commits);
      }
    })

    .then((issues) => {
      buildMetadata.issues = issues;

      if (issues && issues.length !== 0) {
        tools.logInfo('Issues found : ' + buildMetadata.issues);
        tools.logSuccess();
      } else {
        tools.logInfo('No issue found');
      }
    })

    .then(() => {
      return buildMetadata;
    })

    .catch((e) => {
      console.log(e);
      return {};
    })
}


const formatCommit = (commit) => {
  const issueString = commit.issue ? commit.issue : 'NOT PROVIDED';
  return `${commit.date} => issue: ${issueString} - ${commit.originalSubject} by ${commit.user}`;
}




const extractCommits = (pkg, opts) => {

  const SEPARATOR = '===END===';
  const COMMIT_PATTERN = /^(\w*)(?:\((.*)\))?\: (.*)$/;
  const ISSUE_PATTERN = /((?!([A-Z0-9a-z]{1,10})-?$)[A-Z]{1}[A-Z0-9]+-\d+)/g;
  const FORMAT = '%H%n%s%n%ad%n%an%n%b%n%f%n' + SEPARATOR;

  const DEFAULT_OPTS = {
    fromTag: null,
    allRevisions: false,
    toTag: 'HEAD',
    logAll: true
  };

  opts = Object.assign(DEFAULT_OPTS, opts);



  return Promise.resolve()
    .then(() => {
      let tag;

      if (opts.fromTag) {
        tag = opts.fromTag;
      } else {
        if (opts.allRevisions) {
          tag = null;
        } else {
          tag = gitUtils.getLastTag(pkg.paths.root);
        }
      }
      return tag;
    })
    .then((tag) => {
      let revisions;

      if (tag) {
        tag = tag.toString().trim();
        revisions = tag ? tag + '..' + opts.toTag : '';

        if (opts.logAll) tools.logInfo('git log command : ' + 'git log -E --date=short --format=' + FORMAT + ' ' + revisions);
        return execa.sync('git', ['log', '-E', '--date=short', '--format=' + FORMAT, revisions], { cwd: pkg.paths.root }).stdout;
      } else {
        if (opts.logAll) tools.logInfo('git log command : ' + 'git log -E --date=short --format=' + FORMAT);
        return execa.sync('git', ['log', '-E', '--date=short', '--format=' + FORMAT], { cwd: pkg.paths.root }).stdout;
      }
    })
    .then((commits) => {
      return commits.split('\n' + SEPARATOR + '\n');
    })
    .then((raws) => {
      return raws.map((raw) => {
        if (!raw) {
          return null;
        }

        let lines = raw.split('\n');
        let commit = {};

        commit.hash = lines[0];
        commit.subject = lines[1];
        commit.date = lines[2];
        commit.user = lines[3];
        commit.body = lines[4];

        commit.breakingChange = false;

        if (commit.body) {
          let parsedIssueBody = commit.body.match(ISSUE_PATTERN);
          if (parsedIssueBody) {
            commit.issue = parsedIssueBody[0];
          }
          if (commit.body.indexOf('BREAKING CHANGE') !== -1) {
            commit.breakingChange = true;
          }
        }

        if (opts.logAll) tools.logInfo('parsing commit : ' + commit.date + ' ==> ' + commit.subject);

        commit.originalSubject = commit.subject;

        let parsed = commit.subject.match(COMMIT_PATTERN);
        let parsedIssue = commit.subject.match(ISSUE_PATTERN);

        if (!parsed || !parsed[1]) {
          if (commit.subject.indexOf('Merge pull request') === -1 &&
            commit.subject.indexOf('Merge branch') === -1 &&
            commit.subject.indexOf('Merge remote-tracking branch') === -1) {
            tools.logError('INVALID COMMIT MESSAGE FORMAT : ' + formatCommit(commit));
          }
          return null;
        }

        commit.type = parsed[1].toLowerCase();
        if (parsed[2] !== undefined) {
          commit.category = parsed[2].substr(0) || '';
        }
        commit.subject = parsed[3];

        if (parsedIssue) {
          commit.issue = parsedIssue[0];
        }

        if (!commit.issue) {
          commit.issue = '';
        }

        if (commit.type === 'chore' && commit.category === 'release') {
          return null;
        }

        tools.logSuccess('VALID COMMIT MESSAGE : ' + formatCommit(commit));

        return commit;
      })
    })
    .then((parsedCommits) => {
      return parsedCommits.filter((commit) => {
        if (!commit) {
          return false;
        }
        return commit;
      })
    })
    .then((finalCommits) => {
      return tools.removeArrayDuplicates(finalCommits, 'subject');
    })
    .catch((e) => {
      throw e;
    })
};


const extractIssuesFromCommits = (commits) => {
  return Promise.resolve()
    .then(() => {
      return commits.map((commit) => {
        return commit.issue;
      }).filter((issue) => {
        if (issue === '') {
          return false;
        }
        return issue;
      }).filter((elem, pos, arr) => {
        return arr.indexOf(elem) == pos;
      });
    })
    .catch((e) => {
      throw e;
    });
}


const checkCommitsMetadata = (metadata) => {

  return Promise.resolve()
    // checking commit gates
    .then(() => {
      tools.logTitle('checking commit gates...');

      // checking if at least one valid commit message has been provide since last tagged release
      if (!metadata.commits || metadata.commits.length === 0) {
        tools.logError('ERROR : no valid commit found since last release');
        if (!skipCommitChecks) {
          throw new Error('NO_VALID_COMMIT_FOUND');
        }
      }

      // checking if at least one issue key has been provided since last tagged release
      if (!metadata.issues || metadata.issues.length === 0) {
        tools.logError('ERROR : no issue found since last release');
        if (!skipCommitChecks) {
          throw new Error('NO_ISSUE_FOUND');
        }
      }

      tools.logSuccess();
    })
    .catch((e) => {
      throw e;
    })
}


module.exports = {
  getMetadata,
  formatCommit,
  checkCommitsMetadata,
}
