text: /* global Vuex */
  const filesSortDict = {
    lineOfCode: (file) => file.lineCount,
    path: (file) => file.path,
    fileName: (file) => file.path.split(/[/]+/).pop(),
    fileType: (file) => file.fileType,
  };
  
  const repoCache = [];
  const minimatch = require('minimatch');
  
  window.vAuthorship = {
    props: ['info'],
    template: window.$('v_authorship').innerHTML,
    data() {
      return {
        isLoaded: false,
        files: [],
        filterType: 'checkboxes',
        selectedFileTypes: [],
        fileTypes: [],
        filesLinesObj: {},
        fileTypeBlankLinesObj: {},
        filesSortType: 'lineOfCode',
        toReverseSortFiles: true,
        searchBarValue: '',
      };
    },
  
    watch: {
      filesSortType() {
        window.addHash('authorshipSortBy', this.filesSortType);
        window.encodeHash();
      },
  
      toReverseSortFiles() {
        window.addHash('reverseAuthorshipOrder', this.toReverseSortFiles);
        window.encodeHash();
      },
  
      isLoaded() {
        if (this.isLoaded) {
          this.retrieveHashes();
          this.setInfoHash();
        }
      },
    },
  
    methods: {
      retrieveHashes() {
        window.decodeHash();
        const hash = window.hashParams;
  
        switch (hash.authorshipSortBy) {
        case 'path':
        case 'fileName':
        case 'fileType':
          this.filesSortType = hash.authorshipSortBy;
          break;
        default:
          // Invalid value, use the default value of 'lineOfCode'
        }
  
        this.toReverseSortFiles = hash.reverseAuthorshipOrder !== 'false';
  
        this.selectedFileTypes = this.info.checkedFileTypes
          ? this.info.checkedFileTypes.filter((fileType) => this.fileTypes.includes(fileType))
          : [];
        if (hash.authorshipFileTypes) {
          this.selectedFileTypes = hash.authorshipFileTypes
              .split(window.HASH_DELIMITER)
              .filter((fileType) => this.fileTypes.includes(fileType));
        }
  
        if ('authorshipFilesGlob' in hash) {
          this.indicateSearchBar();
          this.searchBarValue = hash.authorshipFilesGlob;
        }
      },
  
      setInfoHash() {
        const { addHash } = window;
        // We only set these hashes as they are propagated from summary_charts
        addHash('tabAuthor', this.info.author);
        addHash('tabRepo', this.info.repo);
        addHash('authorshipIsMergeGroup', this.info.isMergeGroup);
        this.updateFileTypeHash();
      },
  
      removeAuthorshipHashes() {
        window.removeHash('authorshipFileTypes');
        window.removeHash('authorshipFilesGlob');
        window.removeHash('authorshipSortBy');
        window.removeHash('reverseAuthorshipOrder');
        window.removeHash('tabAuthor');
        window.removeHash('tabRepo');
        window.removeHash('authorshipIsMergeGroup');
        window.encodeHash();
      },
  
      initiate() {
        const repo = window.REPOS[this.info.repo];
  
        this.getRepoProps(repo);
        if (!repo || !this.info.author) {
          window.app.isTabActive = false;
          return;
        }
        if (repoCache.length === 2) {
          const toRemove = repoCache.shift();
          if (toRemove !== this.info.repo) {
            delete window.REPOS[toRemove].files;
          }
        }
        repoCache.push(this.info.repo);
  
        if (repo.files) {
          this.processFiles(repo.files);
        } else {
          window.api.loadAuthorship(this.info.repo)
              .then((files) => this.processFiles(files));
        }
      },
  
      getRepoProps(repo) {
        if (repo) {
          if (this.info.isMergeGroup) {
            // sum of all users' file type contribution
            repo.users.forEach((author) => {
              this.updateTotalFileTypeContribution(author.fileTypeContribution);
            });
          } else {
            const author = repo.users.find((user) => user.name === this.info.author);
            if (author) {
              this.info.name = author.displayName;
              this.filesLinesObj = author.fileTypeContribution;
            }
          }
        }
      },
  
      updateTotalFileTypeContribution(fileTypeContribution) {
        Object.entries(fileTypeContribution).forEach(([type, cnt]) => {
          if (this.filesLinesObj[type]) {
            this.filesLinesObj[type] += cnt;
          } else {
            this.filesLinesObj[type] = cnt;
          }
        });
      },
  
      expandAll() {
        this.selectedFiles.forEach((file) => {
          file.active = true;
          file.wasCodeLoaded = true;
        });
      },
  
      collapseAll() {
        this.selectedFiles.forEach((file) => {
          file.active = false;
        });
      },
  
      toggleFileActiveProperty(file) {
        file.active = !file.active;
        file.wasCodeLoaded = file.wasCodeLoaded || file.active;
      },
  
      isUnknownAuthor(name) {
        return name === '-';
      },
  
      hasCommits(info) {
        const { isMergeGroup, author } = info;
        const repo = window.REPOS[info.repo];
        if (repo) {
          return isMergeGroup
              ? Object.entries(repo.commits.authorFinalContributionMap).some(([name, cnt]) => (
                !this.isUnknownAuthor(name) && cnt > 0
              ))
              : repo.commits.authorFinalContributionMap[author] > 0;
        }
        return false;
      },
  
      splitSegments(lines) {
        // split into segments separated by authored
        let lastState;
        let lastId = -1;
        const segments = [];
        let blankLineCount = 0;
  
        lines.forEach((line, lineCount) => {
          const isAuthorMatched = this.info.isMergeGroup
              ? !this.isUnknownAuthor(line.author.gitId)
              : line.author.gitId === this.info.author;
          const authored = (line.author && isAuthorMatched);
  
          if (authored !== lastState || lastId === -1) {
            segments.push({
              authored,
              lines: [],
              lineNumbers: [],
            });
  
            lastId += 1;
            lastState = authored;
          }
  
          const content = line.content || ' ';
          segments[lastId].lines.push(content);
  
          segments[lastId].lineNumbers.push(lineCount + 1);
  
          if (line.content === '' && authored) {
            blankLineCount += 1;
          }
        });
  
        return {
          segments,
          blankLineCount,
        };
      },
  
      processFiles(files) {
        const COLLAPSED_VIEW_LINE_COUNT_THRESHOLD = 2000;
        const res = [];
        const fileTypeBlanksInfoObj = {};
  
        files.forEach((file) => {
          const contributionMap = file.authorContributionMap;
          const lineCnt = this.info.isMergeGroup
              ? this.getContributionFromAllAuthors(contributionMap)
              : contributionMap[this.info.author];
  
          if (lineCnt) {
            const out = {};
            out.path = file.path;
            out.lineCount = lineCnt;
            out.active = lineCnt <= COLLAPSED_VIEW_LINE_COUNT_THRESHOLD;
            out.wasCodeLoaded = lineCnt <= COLLAPSED_VIEW_LINE_COUNT_THRESHOLD;
            out.fileType = file.fileType;
  
            const segmentInfo = this.splitSegments(file.lines);
            out.segments = segmentInfo.segments;
            out.blankLineCount = segmentInfo.blankLineCount;
  
            this.addBlankLineCount(file.fileType, segmentInfo.blankLineCount,
                fileTypeBlanksInfoObj);
            res.push(out);
          }
        });
  
        res.sort((a, b) => b.lineCount - a.lineCount);
  
        Object.keys(this.filesLinesObj).forEach((file) => {
          if (this.filesLinesObj[file] !== 0) {
            this.fileTypes.push(file);
          }
        });
  
        this.fileTypeBlankLinesObj = fileTypeBlanksInfoObj;
        this.files = res;
        this.isLoaded = true;
      },
  
      getContributionFromAllAuthors(contributionMap) {
        return Object.entries(contributionMap).reduce((acc, [author, cnt]) => (
          (!this.isUnknownAuthor(author) ? acc + cnt : acc)
        ), 0);
      },
  
      addBlankLineCount(fileType, lineCount, filesInfoObj) {
        if (!filesInfoObj[fileType]) {
          filesInfoObj[fileType] = 0;
        }
  
        filesInfoObj[fileType] += lineCount;
      },
  
      updateSearchBarValue() {
        this.searchBarValue = this.$refs.searchBar.value;
  
        window.addHash('authorshipFilesGlob', this.searchBarValue);
        window.removeHash('authorshipFileTypes');
        window.encodeHash();
      },
  
      updateFileTypeHash() {
        const fileTypeHash = this.selectedFileTypes.length > 0
            ? this.selectedFileTypes.reduce((a, b) => `${a}~${b}`)
            : '';
  
        window.addHash('authorshipFileTypes', fileTypeHash);
        window.removeHash('authorshipFilesGlob');
        window.encodeHash();
      },
  
      indicateSearchBar() {
        this.selectedFileTypes = this.fileTypes.slice();
        this.filterType = 'search';
      },
  
      indicateCheckBoxes() {
        this.searchBarValue = '';
        this.filterType = 'checkboxes';
        this.updateFileTypeHash();
      },
  
      getFileLink(file, path) {
        const repo = window.REPOS[this.info.repo];
  
        return `${window.BASE_URL}/${
          repo.location.organization}/${repo.location.repoName}/${path}/${repo.branch}/${file.path}`;
      },
  
      getFileTypeBlankLineInfo(fileType) {
        return `${fileType}: Blank: ${
          this.fileTypeBlankLinesObj[fileType]}, Non-Blank: ${
          this.filesLinesObj[fileType] - this.fileTypeBlankLinesObj[fileType]}`;
      },
  
      getTotalFileBlankLineInfo() {
        return `Total: Blank: ${this.totalBlankLineCount}, Non-Blank: ${
          this.totalLineCount - this.totalBlankLineCount}`;
      },
    },
  
    computed: {
      sortingFunction() {
        return (a, b) => (this.toReverseSortFiles ? -1 : 1)
          * window.comparator(filesSortDict[this.filesSortType])(a, b);
      },
  
      isSelectAllChecked: {
        get() {
          return this.selectedFileTypes.length === this.fileTypes.length;
        },
        set(value) {
          if (value) {
            this.selectedFileTypes = this.fileTypes.slice();
          } else {
            this.selectedFileTypes = [];
          }
  
          this.indicateCheckBoxes();
        },
      },
  
      selectedFiles() {
        return this.files.filter((file) => this.selectedFileTypes.includes(file.fileType)
            && minimatch(file.path, this.searchBarValue || '*', { matchBase: true, dot: true }))
            .sort(this.sortingFunction);
      },
  
      activeFilesCount() {
        return this.selectedFiles.filter((file) => file.active).length;
      },
  
      totalLineCount() {
        return Object.values(this.fileTypeLinesObj).reduce((acc, val) => acc + val, 0);
      },
  
      totalBlankLineCount() {
        return Object.values(this.fileTypeBlankLinesObj).reduce((acc, val) => acc + val, 0);
      },
  
      fileTypeLinesObj() {
        const numLinesModified = {};
        Object.entries(this.filesLinesObj)
            .filter(([, value]) => value > 0)
            .forEach(([langType, value]) => {
              numLinesModified[langType] = value;
            });
        return numLinesModified;
      },
  
      ...Vuex.mapState(['fileTypeColors']),
    },
  
    created() {
      this.initiate();
    },
  
    beforeDestroy() {
      this.removeAuthorshipHashes();
    },
  
    components: {
      vSegment: window.vSegment,
    },
  };
  