'use strict';

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

// LOCAL
const tools = require('../../utils/tools');
const pipelineUtils = require('../../utils/pipeline-utils');
const configUtils = require('../config/config-utils');

// INNER MODULES
const innerCommon = require('./common');
const innerApp = require('./app');

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




const getMetadata = (project) => {
  tools.logInfo('Getting app history metadata');

  const DEVOPS_METADATA_PATH = configUtils.global.getConfigOptions().DEVOPS_METADATA_PATH;

  return Promise.resolve()
    .then(() => {
      if (!DEVOPS_METADATA_PATH) {
        throw new Error(`Devops metadata folder ${DEVOPS_METADATA_PATH} NOT FOUND`);
      }

      return tools.getJsonFileContent(
        path.join(DEVOPS_METADATA_PATH, project.devopsHistoryMetadataFile)
      );
    })
    .catch((e) => {
      throw e;
    })
}




const storeMetadata = (project, diffMetadata, newVersion, envTarget) => {
  tools.logInfo('Storing app history metadata');

  const DEVOPS_METADATA_PATH = configUtils.global.getConfigOptions().DEVOPS_METADATA_PATH;

  let flatJiraList = [];
  let historyMetadata = {};

  return Promise.resolve()

    // // flatten jira key list from diffMetadata extracted
    // .then(() => {
    //   diffMetadata.forEach((pkg) => {
    //     pkg.gitMetadata.forEach((gitMetadata) => {
    //       gitMetadata.forEach((v) => {
    //         if (v.issue) flatJiraList.push(v.issue);
    //       })
    //     })
    //   })

    //   flatJiraList = tools.removeArrayDuplicates(flatJiraList);
    // })

    // store the history file
    .then(() => {
      const historyFile = path.join(DEVOPS_METADATA_PATH, project.devopsHistoryMetadataFile);

      let versions = [];

      if (tools.isFileExists(historyFile)) {
        historyMetadata = require(historyFile);
        versions = historyMetadata.versions;
      }

      if (envTarget) {
        versions.push({
          name: newVersion,
          diffs: diffMetadata,
          envTarget: envTarget,
          // issues: flatJiraList
        });

      } else {
        versions.push({
          name: newVersion,
          diffs: diffMetadata,
          // issues: flatJiraList
        });
      }

      historyMetadata.versions = versions;

      if (!dryRun) {
        tools.writeJsonFileSync(historyFile, historyMetadata);
      }

    })

    // // storing issuesList gathered into gitlab variables for post CI processing
    // .then(() => {
    //   if (!flatJiraList) {
    //     tools.logWarning('No issues found for this release');

    //   } else {
    //     const issuesList = flatJiraList.join(',');
    //     tools.logInfo(`Unique issues found for this version : ${issuesList}`);

    //     if (!dryRun) {
    //       return pipelineUtils.setVariables(project.paths.rootPath, `export ISSUES_LIST=${issuesList}\n`);
    //     }
    //   }
    // })


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

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


const getDiffBetweenBuildVersions = (fromBuildMetadata, toBuildMetadata, hasHistory) => {

  tools.logTitle('Fetching diffs history metadata');

  if (!hasHistory) {
    tools.logInfo('App has no history - skipping...');
    return [];
  }

  tools.logInfo(`Fetching diffs between ${fromBuildMetadata.name} and ${toBuildMetadata.name}`);

  return Promise.resolve()
    .then(() => {
      // get differences from old in previous major release to new currently generated
      const diffs = [];
      Object.keys(fromBuildMetadata.packages).forEach((k) => {
        if ({}.hasOwnProperty.call(toBuildMetadata.packages, k) && toBuildMetadata.packages[k] !== fromBuildMetadata.packages[k]) {
          diffs.push({ package: k, old: fromBuildMetadata.packages[k], new: toBuildMetadata.packages[k] });
        }
      });

      // remove snapshots as they are not stored in the package metadata
      const cleanDiffs = diffs.filter((item) => {
        return item.old.indexOf('-snapshot') === -1 &&
               item.new.indexOf('-snapshot') === -1 &&
               item.package.indexOf('@eui') === -1;
      })

      return cleanDiffs;
    })

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



const getDiffByEnvTarget = (project, envTarget) => {
  return Promise.resolve()
    .then(() => {
      return innerApp.getMetadata(project);
    })

    .then((appVersionsMetadata) => {
      const envVersions = appVersionsMetadata.versions.filter((v) => {
        return v.envTarget === envTarget
      });

      const fromVersion = envVersions[envVersions.length - 2];
      const toVersion = envVersions[envVersions.length - 1];
      const hasHistory = fromVersion && toVersion;

      if (!fromVersion || !toVersion) {
        tools.logWarning('WARNING: could not find versions matching for generating diff report');
      } else {
        return getDiffBetweenBuildVersions(fromVersion, toVersion, hasHistory);
      }

    })

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



const getDiffBetweenMajors = (project, fromVersion, toVersion, isSupportBranch) => {

  return Promise.resolve()

    .then(() => {
      return innerApp.getMetadata(project);
    })

    .then((appMetadata) => {
      tools.logInfo(`Checking diffs from ${fromVersion} to ${toVersion}`);

      // we take only the major versions of the app
      const appMajorVersions = appMetadata.versions.filter((version) => {
        if (isSupportBranch) {
          // if support branch we take the given versions provided (latest support on the same major)
          return version.name === fromVersion || version.name === toVersion;
        } else {
          // we take only the major versions of the app
          return version.name.indexOf('.0.0') > -1;
        }
      })

      // Getting the version objects, by default if not provided take the latest previous
      let fromMajorVersionIdx;
      let toMajorVersionIdx;

      if (fromVersion) {
        fromMajorVersionIdx = appMajorVersions.findIndex(v => v.name === fromVersion);
      } else {
        fromMajorVersionIdx = appMajorVersions.length - 2;
      }

      if (toVersion) {
        toMajorVersionIdx = appMajorVersions.findIndex(v => v.name === toVersion);
      } else {
        toMajorVersionIdx = appMajorVersions.length - 1;
      }

      const fromMajorVersion = appMajorVersions[fromMajorVersionIdx];
      const toMajorVersion = appMajorVersions[toMajorVersionIdx];

      const hasHistory = fromMajorVersion && toMajorVersion;

      return getDiffBetweenBuildVersions(fromMajorVersion, toMajorVersion, hasHistory);

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







// get differences of package versions between last(current) and previous major

const generateDiffReport = (project, newVersion, isSupportBranch, envTarget, fromVersion, toVersion) => {
  tools.logTitle('Generate packages differences report from previous major version...');

  return Promise.resolve()
    .then(() => {

      if (envTarget) {
        return getDiffByEnvTarget(project, envTarget);

      } else {
        if (isSupportBranch) {
          return getDiffBetweenMajors(project, semver.major(newVersion) + '.0.0', newVersion, true);
        } else {
          if (fromVersion && toVersion) {
            return getDiffBetweenMajors(project, fromVersion, toVersion);
          } else {
            return getDiffBetweenMajors(project);
          }
        }
      }
    })

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


const flattenDiffMetadata = (metadata) => {
  return Promise.resolve()
    .then(() => {
      return metadata.map((item) => {
        return { package: item.package, fromVersion: item.old, toVersion: item.new };
      });
    })

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

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


const flatDiffMetadataToMessage = () => {
  let message = 'No differences detected';

  if (diffMetadata.length !== 0) {
    message = diffMetadata.map((pkg) => {
      return `${pkg.package} ---> from [${pkg.fromVersion}] to [${pkg.toVersion}]`
    }).join('\n ');
  }

  return message;
}



module.exports.getMetadata = getMetadata;
module.exports.storeMetadata = storeMetadata;
module.exports.getDiffBetweenMajors = getDiffBetweenMajors;
module.exports.generateDiffReport = generateDiffReport;
module.exports.flattenDiffMetadata = flattenDiffMetadata;
module.exports.flatDiffMetadataToMessage = flatDiffMetadataToMessage;
module.exports.getDiffBetweenBuildVersions = getDiffBetweenBuildVersions;

