'use strict';

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

// LOCAL
const tools = require('../../utils/tools');
const configUtils = require('../config/config-utils');
const metadataUtils = require('../metadata/metadata-utils');

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


const groupBy = (xs, f) => {
  return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).push(v), r), {});
}

const LAST_WEEK = moment().subtract(7, "days");
const LAST_MONTH = moment().subtract(30, "days");
const NOW = moment();


module.exports.run = () => {
  tools.logTitle('Extracting metadata stats');

  const stats = {};

  return Promise.resolve()
    .then(() => {
      if (!dryRun && !skipClone) {
        return metadataUtils.common.cloneMetadataRepo();
      }
    })

    .then(() => {
      tools.logInfo('Generating packages stats');

      const csdrPackages = configUtils.packages.getCsdrPackages();
      const packages = Object.keys(csdrPackages).map((p) => configUtils.packages.getPackage(p, true));

      const packagesMetadata = {};
      const users = {};

      // Accumulating all packages metadata in one single object
      packages.forEach((p) => {
        console.log(`Processing package : ${p.name}`);

        const metadataRoot = path.join(process.cwd(), 'packages', 'devops-metadata');
        const pkgMetadata = tools.getJsonFileContent(path.join(metadataRoot, p.devopsMetadataFile));
        const pkgMetadataVersions = tools.getJsonFileContent(path.join(metadataRoot, p.devopsVersionsMetadataFile));

        const gitCommits = [];
        if (pkgMetadata.versions) {
          pkgMetadata.versions.forEach((v) => {
            if (v.gitMetadata.commits) {
              v.gitMetadata.commits.forEach((c) => {
                gitCommits.push(c);
              });
            }
          });
        }

        const commitUsers = groupBy(gitCommits, (c) => c.user.toUpperCase());
        const commitsByUser = Object.keys(commitUsers).map((u) => { return { user: u, commits: commitUsers[u].length } })

        packagesMetadata[p.name] = {
          latest: {
            versions: pkgMetadata.versions || [],
            commits: gitCommits,
            commitsByUser: commitsByUser
          },
          allVersions: pkgMetadataVersions.versions || []
        }

        commitsByUser.forEach((u) => {
          const user = users[u.user];
          if (!user) {
            users[u.user] = { commits: u.commits };
          } else {
            users[u.user].commits = users[u.user].commits + u.commits;
          }
        })
      })


      // Generate users stats
      let usersArray = [];
      Object.keys(users).forEach((u) => {
        usersArray.push({ user: u, commits: users[u].commits });
      })

      // Generate packages grouped stats
      const packagesArray = Object.keys(packagesMetadata).map((p) => {
        return { name: p, metadata: packagesMetadata[p] };
      });

      // versions count per package
      const packageVersionsCount = packagesArray.map((p) => {
        return { name: p.name, versionsCount: p.metadata.allVersions.length };
      })

      // package not having been released as latest
      const packagesNotLatest = packagesArray.filter((p) => {
        return p.metadata.latest.versions.length === 0;
      }).map(p => p.name);

      // constructing final packages stats
      stats.packages = {
        packageVersionsCount: tools.sortArray(packageVersionsCount, 'versionsCount', 'desc'),
        packagesNotLatest: packagesNotLatest,
        users: tools.sortArray(usersArray, 'commits', 'desc')
      }
    })

    .then(() => {
      console.log(stats);

      return tools.writeJsonFileSync(path.join(process.cwd(), 'csdr-stats.json'), stats);
    })

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


module.exports.getAllPackagesMetadata = (packages) => {
  return Promise.resolve()
    .then(() => {
      const metadataPackages = packages.map((p) => {
        const metadataRoot = path.join(process.cwd(), 'packages', 'devops-metadata');
        const pkgMetadata = tools.getJsonFileContent(path.join(metadataRoot, p.devopsMetadataFile));
        const pkgMetadataVersions = tools.getJsonFileContent(path.join(metadataRoot, p.devopsVersionsMetadataFile));

        return {
          package: p,
          metadata: pkgMetadata,
          versions: pkgMetadataVersions,
        }
      })

      console.log(metadataPackages);
      return metadataPackages;
    })

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

module.exports.getAllPackagesVersions = (packages) => {
  return Promise.resolve()
    .then(() => {
      const pkgVersions = packages.map((p) => {
        const metadataRoot = path.join(process.cwd(), 'packages', 'devops-metadata');
        const pkgMetadataVersions = tools.getJsonFileContent(path.join(metadataRoot, p.devopsVersionsMetadataFile));

        return {
          name: p.name,
          versions: pkgMetadataVersions.versions,
        }
      })

      return pkgVersions;
    })

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


const getPackagesVersionsByDates = (pkgVersions, range = 'ALL') => {
  const versionDates = [];

  pkgVersions.forEach( p => {
    if (p.versions) {
      p.versions.forEach( v => {
        const dt = v.date.substr(0, 8);

        if (range === 'ALL') {
          versionDates.push(dt);

        } else if (range === 'LAST_WEEK') {
          const isBetween = moment(dt).isBetween(LAST_WEEK, NOW);
          if (isBetween) {
            versionDates.push(dt);
          }

        } else if (range === 'LAST_MONTH') {
          const isBetween = moment(dt).isBetween(LAST_MONTH, NOW);
          if (isBetween) {
            versionDates.push(dt);
          }
        }

      })
    }
  });

  const versionsGrouped = groupBy(versionDates, (v) => v);
  const versionsByDates = Object.keys(versionsGrouped).map((v) => { return { date: v, versions: versionsGrouped[v].length } })

  return versionsByDates;
}



module.exports.getVersionsByDates = (packages, range = 'ALL') => {
  const output = {};

  return Promise.resolve()
  .then(() => {
    return this.getAllPackagesVersions(packages.filter(p => { return p.backend }));
  })
  .then((pkgVersions) => {
    output.packagesBackend = getPackagesVersionsByDates(pkgVersions, range);
  })

  .then(() => {
    return this.getAllPackagesVersions(packages.filter(p => { return !p.backend }));
  })
  .then((pkgVersions) => {
    output.packagesUI = getPackagesVersionsByDates(pkgVersions, range);
  })
  .then(() => {
    return output;
  })
}




module.exports.getPackagesVersions = (packages, range = 'ALL') => {
  const output = {};

  return Promise.resolve()
  .then(() => {
    return this.getAllPackagesVersions(packages);
  })

  .then((pkgVersions) => {
    const pkgVersionsFiltered = [];

    pkgVersions.forEach( p => {
      if (p.versions) {
        const versions = [];

        p.versions.forEach( v => {
          const dt = v.date.substr(0, 8);

          if (range === 'ALL') {
            versions.push(v);

          } else if (range === 'LAST_WEEK') {
            const isBetween = moment(dt).isBetween(LAST_WEEK, NOW);
            if (isBetween) {
              versions.push(v);
            }

          } else if (range === 'LAST_MONTH') {
            const isBetween = moment(dt).isBetween(LAST_MONTH, NOW);
            if (isBetween) {
              versions.push(v);
            }
          }

        })

        pkgVersionsFiltered.push({ name: p.name, versions: versions.length });
      }
    });

    return pkgVersionsFiltered;
  })
  .then((filtered) => {
    return tools.sortArray(filtered, 'versions', 'desc');
  })
}







module.exports.getAllPackagesMetadataVersions = (packages) => {
  return Promise.resolve()
    .then(() => {
      const pkgVersions = packages.map((p) => {
        const metadataRoot = path.join(process.cwd(), 'packages', 'devops-metadata');
        const pkgMetadata = tools.getJsonFileContent(path.join(metadataRoot, p.devopsMetadataFile));

        return {
          name: p.name,
          versions: pkgMetadata.versions,
        }
      })

      return pkgVersions;
    })

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


module.exports.getProjectVersions = (project) => {
  return Promise.resolve()
  .then(() => {
    const metadataRoot = path.join(process.cwd(), 'packages', 'devops-metadata');
    const prjVersions = tools.getJsonFileContent(path.join(metadataRoot, project.devopsVersionsMetadataFile));

    return prjVersions.versions;
  })
}




module.exports.getProjectVersionsByDates = (project, range = 'ALL') => {
  return Promise.resolve()
  .then(() => {
    return this.getProjectVersions(project);
  })

  .then((prjVersions) => {
    const versionDates = [];

    prjVersions.forEach(v => {
      const dt = moment(v.releaseDate, 'DD/MM/YYYY').format('YYYYMMDD');
      // console.log(v.releaseDate, dt);

      if (range === 'ALL') {
        versionDates.push(dt);

      } else if (range === 'LAST_WEEK') {
        const isBetween = moment(dt).isBetween(LAST_WEEK, NOW);
        if (isBetween) {
          versionDates.push(dt);
        }

      } else if (range === 'LAST_MONTH') {
        const isBetween = moment(dt).isBetween(LAST_MONTH, NOW);
        if (isBetween) {
          versionDates.push(dt);
        }
      }
    })

    const versionsGrouped = groupBy(versionDates, (v) => v);
    const versionsByDates = Object.keys(versionsGrouped).map((v) => { return { date: v, versions: versionsGrouped[v].length } })

    return versionsByDates;
  })
}





module.exports.getProjectVersionsByEnvs = (project, range = 'ALL') => {
  return Promise.resolve()
  .then(() => {
    return this.getProjectVersions(project);
  })

  .then((prjVersions) => {
    const versionsEnvs = [];

    prjVersions.forEach(v => {
      const dt = moment(v.releaseDate, 'DD/MM/YYYY').format('YYYYMMDD');

      if (range === 'ALL') {
        versionsEnvs.push(v.envTarget);

      } else if (range === 'LAST_WEEK') {
        const isBetween = moment(dt).isBetween(LAST_WEEK, NOW);
        if (isBetween) {
          versionsEnvs.push(v.envTarget);
        }

      } else if (range === 'LAST_MONTH') {
        const isBetween = moment(dt).isBetween(LAST_MONTH, NOW);
        if (isBetween) {
          versionsEnvs.push(v.envTarget);
        }
      }
    })

    const versionsGrouped = groupBy(versionsEnvs, (v) => v);
    const versionsByEnvs = Object.keys(versionsGrouped).map((v) => { return { env: v, versions: versionsGrouped[v].length } })

    return versionsByEnvs;
  })
}






const getPackagesCommitsByUsers = (pkgVersions, range = 'ALL') => {
  const users = {};

  const gitCommits = [];

  pkgVersions.forEach( p => {
    if (p.versions) {
      p.versions.forEach((v) => {
        v.gitMetadata.commits.forEach((c) => {

          if (range === 'ALL') {
            gitCommits.push(c);

          } else if (range === 'LAST_WEEK') {
            const isBetween = moment(c.date).isBetween(LAST_WEEK, NOW);
            if (isBetween) {
              gitCommits.push(c);
            }

          } else if (range === 'LAST_MONTH') {
            const isBetween = moment(c.date).isBetween(LAST_MONTH, NOW);
            if (isBetween) {
              gitCommits.push(c);
            }
          }

        });
      });
    }
  });

  const commitUsers = groupBy(gitCommits, (c) => c.user.toUpperCase());
  const commitsByUser = Object.keys(commitUsers).map((u) => { return { user: u, commits: commitUsers[u].length } })

  console.log(commitUsers);

  return commitsByUser;
}


module.exports.getCommitsByUsers = (packages, range = 'ALL') => {
  return Promise.resolve()
  .then(() => {
    return this.getAllPackagesMetadataVersions(packages);
  })
  .then((pkgVersions) => {
    const usersArray = getPackagesCommitsByUsers(pkgVersions, range);
    return tools.sortArray(usersArray, 'commits', 'desc');
  })
}


// module.exports.getRemoteAllStats = () => {

//   let stats = [];

//   return Promise.resolve()
//     .then(() => {
//       if (!dryRun && !skipClone && !tools.isDirExists(path.join(process.cwd(), 'packages', 'devops-metadata'))) {
//         return metadataUtils.common.cloneMetadataRepo();
//       }
//     })
//     .then(() => {
//       let remotesArray = [
//         ...configUtils.remotes.getCsdrRemotesArray('my-workplace', '17.x'),
//         ...configUtils.remotes.getCsdrRemotesArray('my-workplace', '16.x'),
//         ...configUtils.remotes.getCsdrRemotesArray('my-workplace', '15.x'),
//       ];

//       remotesArray = tools.sortArray(remotesArray, 'name');

//       remotesArray.forEach((r) => {
//         try {
//           const remote = configUtils.remotes.getRemote(r.name);
//           const versionsMatadata = metadataUtils.packageVersions.getMetadataSync(remote);
//           let versionsCount = 0, versionsCountEnvs = { "DEV": 0, "TST": 0, "INT": 0, "ACC": 0, "TRN": 0, "PROD": 0}, lastVersion = {};
//           if (versionsMatadata.versions) {
//             versionsCount = versionsMatadata.versions.length;
//             versionsCountEnvs['DEV'] = versionsMatadata.versions.filter(v => v.envTarget === 'DEV').length;
//             versionsCountEnvs['TST'] = versionsMatadata.versions.filter(v => v.envTarget === 'TST').length;
//             versionsCountEnvs['INT'] = versionsMatadata.versions.filter(v => v.envTarget === 'INT').length;
//             versionsCountEnvs['ACC'] = versionsMatadata.versions.filter(v => v.envTarget === 'ACC').length;
//             versionsCountEnvs['TRN'] = versionsMatadata.versions.filter(v => v.envTarget === 'TRN').length;
//             versionsCountEnvs['PROD'] = versionsMatadata.versions.filter(v => v.envTarget === 'PROD').length;
//             lastVersion = versionsMatadata.versions.slice(-1).pop();
//           }
//           const remoteStats = {
//             name: r.name,
//             // versions: versionsMatadata.versions,
//             lastVersion,
//             summary: {
//               versionsCount,
//               versionsCountEnvs
//             }
//           }
//           stats.push(remoteStats);
//         } catch(e) {}
//       })
//     })
//     .then(() => {
//       return {
//         stats
//       }
//     })
//     .catch((e) => {
//       throw e;
//     })
// }


// module.exports.getRemoteStats = (remoteName) => {
//   const remote = configUtils.remotes.getRemote(remoteName);

//   let versions;

//   return Promise.resolve()
//     .then(() => {
//       return metadataUtils.packageVersions.getMetadata(remote);
//     })
//     .then((metadata) => {
//       versions = metadata.versions;
//     })
//     .then(() => {
//       return {
//         versions
//       }
//     })
//     .catch((e) => {
//       throw e;
//     })
// }

