<script>
import { mapState, mapActions } from 'vuex';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import { UNFOLD_COUNT, INLINE_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE } from '../constants';
import * as utils from '../store/utils';
import tooltip from '../../vue_shared/directives/tooltip';

const EXPAND_ALL = 0;
const EXPAND_UP = 1;
const EXPAND_DOWN = 2;

const lineNumberByViewType = (viewType, diffLine) => {
  const numberGetters = {
    [INLINE_DIFF_VIEW_TYPE]: line => line?.new_line,
    [PARALLEL_DIFF_VIEW_TYPE]: line => (line?.right || line?.left)?.new_line,
  };
  const numberGetter = numberGetters[viewType];

  return numberGetter && numberGetter(diffLine);
};

export default {
  directives: {
    tooltip,
  },
  components: {
    Icon,
  },
  props: {
    fileHash: {
      type: String,
      required: true,
    },
    contextLinesPath: {
      type: String,
      required: true,
    },
    line: {
      type: Object,
      required: true,
    },
    isTop: {
      type: Boolean,
      required: false,
      default: false,
    },
    isBottom: {
      type: Boolean,
      required: false,
      default: false,
    },
    colspan: {
      type: Number,
      required: false,
      default: 3,
    },
  },
  computed: {
    ...mapState({
      diffViewType: state => state.diffs.diffViewType,
      diffFiles: state => state.diffs.diffFiles,
    }),
    canExpandUp() {
      return !this.isBottom;
    },
    canExpandDown() {
      return this.isBottom || !this.isTop;
    },
  },
  created() {
    this.EXPAND_DOWN = EXPAND_DOWN;
    this.EXPAND_UP = EXPAND_UP;
  },
  methods: {
    ...mapActions('diffs', ['loadMoreLines']),
    getPrevLineNumber(oldLineNumber, newLineNumber) {
      const diffFile = utils.findDiffFile(this.diffFiles, this.fileHash);
      const lines = {
        [INLINE_DIFF_VIEW_TYPE]: diffFile.highlighted_diff_lines,
        [PARALLEL_DIFF_VIEW_TYPE]: diffFile.parallel_diff_lines,
      };
      const index = utils.getPreviousLineIndex(this.diffViewType, diffFile, {
        oldLineNumber,
        newLineNumber,
      });

      return lineNumberByViewType(this.diffViewType, lines[this.diffViewType][index - 2]) || 0;
    },
    callLoadMoreLines(
      endpoint,
      params,
      lineNumbers,
      fileHash,
      isExpandDown = false,
      nextLineNumbers = {},
    ) {
      this.loadMoreLines({ endpoint, params, lineNumbers, fileHash, isExpandDown, nextLineNumbers })
        .then(() => {
          this.isRequesting = false;
        })
        .catch(() => {
          createFlash(s__('Diffs|Something went wrong while fetching diff lines.'));
          this.isRequesting = false;
        });
    },
    handleExpandLines(type = EXPAND_ALL) {
      if (this.isRequesting) {
        return;
      }

      this.isRequesting = true;
      const endpoint = this.contextLinesPath;
      const { fileHash } = this;
      const view = this.diffViewType;
      const oldLineNumber = this.line.meta_data.old_pos || 0;
      const newLineNumber = this.line.meta_data.new_pos || 0;
      const offset = newLineNumber - oldLineNumber;

      const expandOptions = { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset };

      if (type === EXPAND_UP) {
        this.handleExpandUpLines(expandOptions);
      } else if (type === EXPAND_DOWN) {
        this.handleExpandDownLines(expandOptions);
      } else {
        this.handleExpandAllLines(expandOptions);
      }
    },
    handleExpandUpLines(expandOptions) {
      const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions;

      const bottom = this.isBottom;
      const lineNumber = newLineNumber - 1;
      const to = lineNumber;
      let since = lineNumber - UNFOLD_COUNT;
      let unfold = true;

      const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);
      if (since <= prevLineNumber + 1) {
        since = prevLineNumber + 1;
        unfold = false;
      }

      const params = { since, to, bottom, offset, unfold, view };
      const lineNumbers = { oldLineNumber, newLineNumber };
      this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash);
    },
    handleExpandDownLines(expandOptions) {
      const {
        endpoint,
        fileHash,
        view,
        oldLineNumber: metaOldPos,
        newLineNumber: metaNewPos,
        offset,
      } = expandOptions;

      const bottom = true;
      const nextLineNumbers = {
        old_line: metaOldPos,
        new_line: metaNewPos,
      };

      let unfold = true;
      let isExpandDown = false;
      let oldLineNumber = metaOldPos;
      let newLineNumber = metaNewPos;
      let lineNumber = metaNewPos + 1;
      let since = lineNumber;
      let to = lineNumber + UNFOLD_COUNT;

      if (!this.isBottom) {
        const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);

        isExpandDown = true;
        oldLineNumber = prevLineNumber - offset;
        newLineNumber = prevLineNumber;
        lineNumber = prevLineNumber + 1;
        since = lineNumber;
        to = lineNumber + UNFOLD_COUNT;

        if (to >= metaNewPos) {
          to = metaNewPos - 1;
          unfold = false;
        }
      }

      const params = { since, to, bottom, offset, unfold, view };
      const lineNumbers = { oldLineNumber, newLineNumber };
      this.callLoadMoreLines(
        endpoint,
        params,
        lineNumbers,
        fileHash,
        isExpandDown,
        nextLineNumbers,
      );
    },
    handleExpandAllLines(expandOptions) {
      const { endpoint, fileHash, view, oldLineNumber, newLineNumber, offset } = expandOptions;
      const bottom = this.isBottom;
      const unfold = false;
      let since;
      let to;

      if (this.isTop) {
        since = 1;
        to = newLineNumber - 1;
      } else if (bottom) {
        since = newLineNumber + 1;
        to = -1;
      } else {
        const prevLineNumber = this.getPrevLineNumber(oldLineNumber, newLineNumber);
        since = prevLineNumber + 1;
        to = newLineNumber - 1;
      }

      const params = { since, to, bottom, offset, unfold, view };
      const lineNumbers = { oldLineNumber, newLineNumber };
      this.callLoadMoreLines(endpoint, params, lineNumbers, fileHash);
    },
  },
};
</script>

<template>
  <td :colspan="colspan" class="text-center">
    <div class="content js-line-expansion-content">
      <a
        v-if="canExpandUp"
        v-tooltip
        class="cursor-pointer js-unfold unfold-icon d-inline-block pt-2 pb-2"
        data-placement="top"
        data-container="body"
        :title="__('Expand up')"
        @click="handleExpandLines(EXPAND_UP)"
      >
        <icon :size="12" name="expand-up" aria-hidden="true" />
      </a>
      <a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
        <span>{{ s__('Diffs|Show unchanged lines') }}</span>
      </a>
      <a
        v-if="canExpandDown"
        v-tooltip
        class="cursor-pointer js-unfold-down has-tooltip unfold-icon d-inline-block pt-2 pb-2"
        data-placement="top"
        data-container="body"
        :title="__('Expand down')"
        @click="handleExpandLines(EXPAND_DOWN)"
      >
        <icon :size="12" name="expand-down" aria-hidden="true" />
      </a>
    </div>
  </td>
</template>
