'use strict';

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

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

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



const getPackageVersions = (module.exports.getPackageVersions = (pkg) => {
  let npmOutput;
  try {
    npmOutput = execa.sync('npm', ['view', pkg.npmPkg, '--json']).stdout;
    if (npmOutput.trim() === '') {
      npmOutput = execa.sync('npm', ['view', `${pkg.npmPkg}@snapshot`, '--json']).stdout;
    }
  } catch(e) {
    npmOutput = e.stdout;
  }
  // console.log(npmOutput);

  let versions = [];

  if (!npmOutput) {
    tools.logWarning('Cannot find version of pkg - package is probably not published yet');
    return versions;
  }

  const json = JSON.parse(npmOutput);

  if (json.error) {
    tools.logWarning('Error detected while trying to fetch the pkg version');
    console.log(json.error);

  } else {
    if (json.versions) {
      versions = json.versions.map((v) => {
        return {
          name: pkg.name,
          version: v,
          major: v.split('.')[0],
          snapshot: v.indexOf('snapshot') > -1,
          hotfix: v.indexOf('snapshot') === -1 && v.indexOf('-rc.') === -1 && v.indexOf('-next.') === -1 && v.indexOf('-') > -1,
          rc: v.indexOf('-rc.') > -1,
          date: json.time[v],
        };
      });
    }
  }

  if (versions.length === 0) {
    tools.logWarning(`Pkg versions not found for ${pkg.name} - ${pkg.npmPkg}`);
  }

  return versions;
});


const getPackageVersionsByNpmPkg = (module.exports.getPackageVersionsByNpmPkg = (npmPkg) => {
  let npmOutput;
  try {
    npmOutput = execa.sync('npm', ['view', npmPkg, '--json']).stdout;
    if (npmOutput.trim() === '') {
      npmOutput = execa.sync('npm', ['view', `${npmPkg}@snapshot`, '--json']).stdout;
    }
  } catch(e) {
    npmOutput = e.stdout;
  }
  // console.log(npmOutput);

  let versions = [];

  if (!npmOutput) {
    tools.logWarning('Cannot find version of pkg - package is probably not published yet');
    return versions;
  }

  const json = JSON.parse(npmOutput);

  if (json.error) {
    tools.logWarning('Error detected while trying to fetch the pkg version');
    console.log(json.error);

  } else {
    if (json.versions) {
      versions = json.versions.map((v) => {
        return {
          name: npmPkg,
          version: v,
          major: v.split('.')[0],
          snapshot: v.indexOf('snapshot') > -1,
          hotfix: v.indexOf('snapshot') === -1 && v.indexOf('-rc.') === -1 && v.indexOf('-next.') === -1 && v.indexOf('-') > -1,
          rc: v.indexOf('-rc.') > -1,
          date: json.time[v],
        };
      });
    }
  }

  if (versions.length === 0) {
    tools.logWarning(`Pkg versions not found for ${npmPkg}`);
  }

  return versions;
});

module.exports.getPackageVersionsLatest = (pkg) => {
  const versions = getPackageVersions(pkg);
  return versions.filter((v) => !v.snapshot);
};

module.exports.getPackageVersionLast = (pkg) => {
  const versions = getPackageVersions(pkg);
  if (versions && versions.length !== 0) {
    return versions.splice(-1)[0].version;
  }
};

module.exports.getPackageVersionsLastLatest = (pkg) => {
  const versions = getPackageVersions(pkg);

  if (versions && versions.length !== 0) {
    const versionsLatest = versions.filter((v) => !v.snapshot && !v.hotfix && !v.rc);
    if (versionsLatest && versionsLatest.length !== 0) {
      return versionsLatest.splice(-1)[0].version;
    }
  }
};

module.exports.getPackageVersionsLastRc = (pkg) => {
  const versions = this.getPackageVersions(pkg);

  if (versions && versions.length !== 0) {
    const versionsRc = versions.filter((v) => v.rc);
    if (versionsRc && versionsRc.length !== 0) {
      return versionsRc.splice(-1)[0].version;
    }
  }
};

const getPackageVersionsSnapshot = (pkg) => {
  const versions = getPackageVersions(pkg);
  return versions.filter((v) => v.snapshot);
};

module.exports.getPackageVersionsLastSnapshot = (pkg) => {
  const versions = getPackageVersionsSnapshot(pkg);
  if (versions && versions.length !== 0) {
    return versions.splice(-1)[0].version;
  }
};

const getPackageVersionFirst = (pkg) => {
  const versions = getPackageVersions(pkg);

  if (versions && versions.length !== 0) {
    return versions[0];
  }
};

module.exports.isNewPackageBuild = (pkg) => {
  tools.logInfo('Checking if package is a newly created one : first build date after 2023/06/01 or never built for MASTER');

  const firstPackageVersion = getPackageVersionFirst(pkg);
  const versionsLatest = this.getPackageVersionsLatest(pkg);

  if (firstPackageVersion) {
    if (versionsLatest && versionsLatest.length === 0) {
      tools.logInfo(`==> package has never been built for MASTER / latest version, package is considered new`);
      return true;
    } else {
      const pkgDate = firstPackageVersion.date;

      const pivotDate = moment('20250701');
      const pkgDateM = moment(pkgDate, 'YYYYMMDD-HH:mm');
      tools.logInfo(`First package build version date : ${pkgDate}`);
      tools.logInfo(`==> NEW package : ${pkgDateM > pivotDate}`);
      return pkgDateM > pivotDate;
    }
  } else {
    tools.logInfo(`==> no versions metadata found : first build, package is new`);
    return true;
  }
};



const filterMajorVersions = (pkgVersions, major, isMaster, isDevEnvTarget) => {
  if (debug) tools.logInfo(`----> ${pkgVersions.length} metadata versions found for package`);

  let versions = [];
  versions = pkgVersions.filter((v) => v.major === major);

  if (debug) tools.logInfo(`----> ${versions.length} metadata versions found for major version : ${major}`);

  if (isMaster) {
    versions = versions.filter((v) => !v.snapshot);
    if (debug) tools.logInfo(`----> ${versions.length} metadata versions found excluding snapshots`);
  } else {
    versions = pkgVersions.filter((v) => v.major === major);
    if (debug) tools.logInfo(`----> ${versions.length} metadata versions non-master release for major`);
  }

  if (debug) tools.logInfo('----> exluding hotfix package versions type');
  versions = versions.filter((v) => !v.hotfix);
  if (debug) tools.logInfo(`----> ${versions.length} metadata versions excluding hotfix version type`);

  if (!isDevEnvTarget) {
    if (debug) {
      tools.logInfo(
        '----=> excluding RC release of versions found for non-DEV build (preventing RC releases to go to another env than DEV'
      );
    }
    versions = versions.filter((v) => !v.rc);
    if (debug) tools.logInfo(`----> ${versions.length} metadata versions excluding RC version type`);
  }

  return versions;
}


const getPackageVersionsByMajor = (pkg, major, isMaster, isDevEnvTarget) => {
  const pkgVersions = getPackageVersions(pkg);

  if (pkgVersions) {
    return filterMajorVersions(pkgVersions, major, isMaster, isDevEnvTarget);
  }
};

const getPackageVersionsByMajorByNpmPkg = (npmPkg, major, isMaster, isDevEnvTarget) => {
  const pkgVersions = getPackageVersionsByNpmPkg(npmPkg);

  if (pkgVersions) {
    return filterMajorVersions(pkgVersions, major, isMaster, isDevEnvTarget);
  }
};

module.exports.getLastMajorVersion = (pkg, major, isMaster, isDevEnvTarget) => {
  tools.logInfo(`--Getting registry pkg version for ${pkg.name} - major: ${major}`);

  const pkgVersionsMajor = getPackageVersionsByMajor(pkg, major, isMaster, isDevEnvTarget);

  if (pkgVersionsMajor && pkgVersionsMajor.length !== 0) {
    return pkgVersionsMajor.splice(-1)[0].version;
  }
};

module.exports.getLastMajorVersionByNpmPkg = (npmPkg, major, isMaster, isDevEnvTarget) => {
  tools.logInfo(`--Getting registry pkg version for ${npmPkg} - major: ${major}`);

  const pkgVersionsMajor = getPackageVersionsByMajorByNpmPkg(npmPkg, major, isMaster, isDevEnvTarget);

  if (pkgVersionsMajor && pkgVersionsMajor.length !== 0) {
    return pkgVersionsMajor.splice(-1)[0].version;
  }
};

module.exports.getVersionMetadata = (pkg, version) => {
  const pkgVersions = getPackageVersions(pkg);
  if (pkgVersions) {
    return pkgVersions.filter((v) => {
      return v.version === version;
    })[0];
  } else {
    tools.logWarning('no package metadata found for ' + pkg.name);
  }
};

module.exports.lockPackage = (pkg) => {
  tools.logTitle('Locking package');

  return Promise.resolve()
    .then(() => {
      const configOptions = configUtils.global.getConfigOptions();
      const pkgLockFile = path.join(configOptions.DEVOPS_METADATA_LOCKS_PATH, `${pkg.name}-lock.json`);

      const lockContent = {
        locked: true,
        lockedDate: moment().format(),
      };

      tools.writeJsonFileSync(pkgLockFile, lockContent);

      if (!dryRun) {
        tools.logTitle('Commiting metadata info');
        return gitUtils.commitAndPush('master', `chore: locking ${pkg.name}`, configOptions.DEVOPS_METADATA_LOCKS_PATH);
      }
    })

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

module.exports.unlockPackage = (pkg) => {
  tools.logTitle('Unlocking package');

  return Promise.resolve()
    .then(() => {
      const configOptions = configUtils.global.getConfigOptions();
      const pkgLockFile = path.join(configOptions.DEVOPS_METADATA_LOCKS_PATH, `${pkg.name}-lock.json`);

      tools.remove(pkgLockFile);

      if (!dryRun) {
        tools.logTitle('Commiting metadata info');
        return gitUtils.commitAndPush('master', `chore: unlocking ${pkg.name}`, configOptions.DEVOPS_METADATA_LOCKS_PATH);
      }
    })

    .catch((e) => {
      tools.logError('Unable to unlock package => package will be auto-unlock at next build 10m after the current one');
    });
};

module.exports.isPackageLocked = (pkg) => {
  const configOptions = configUtils.global.getConfigOptions();
  const pkgLockFile = path.join(configOptions.DEVOPS_METADATA_LOCKS_PATH, `${pkg.name}-lock.json`);

  if (dryRun) return false;

  if (tools.isFileExists(pkgLockFile)) {
    const lockContent = tools.getJsonFileContent(pkgLockFile);

    try {
      if (lockContent.locked) {
        const lockedDate = moment(lockContent.lockedDate);
        const currentDate = moment();
        const diff = currentDate.diff(lockedDate, 'm');

        tools.logInfo(`checking diff current date from locked date : ${diff}`);

        if (diff > 10) {
          tools.logInfo(`> 10m => ok forcing unlock`);
          return false;
        } else {
          tools.logInfo(`< 10m => keep lock`);
          return true;
        }
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  }

  return false;
};
