<script>
import { GlTooltipDirective, GlTable } from '@gitlab/ui';
import { GlSparklineChart } from '@gitlab/ui/dist/charts';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import { firstAndLastY } from '~/lib/utils/chart_utils';
import {
  formatDate,
  differenceInMilliseconds,
  millisecondsPerDay,
} from '~/lib/utils/datetime_utility';
import { formattedChangeInPercent } from '~/lib/utils/number_utils';
import { s__, sprintf } from '~/locale';
import { SEVERITY_LEVELS, DAYS } from '../store/constants';
import ChartButtons from './vulnerability_chart_buttons.vue';

const ISO_DATE = 'isoDate';

export default {
  components: {
    ChartButtons,
    GlSparklineChart,
    GlTable,
    SeverityBadge,
  },
  directives: {
    GlTooltip: GlTooltipDirective,
  },
  props: {
    query: { type: Object, required: true },
    groupFullPath: { type: String, required: false, default: undefined },
  },
  data() {
    return {
      vulnerabilitiesHistory: {},
      vulnerabilitiesHistoryDayRange: DAYS.thirty,
      errorLoadingVulnerabilitiesHistory: false,
    };
  },
  days: Object.values(DAYS),
  fields: [
    { key: 'severityLevel', label: s__('VulnerabilityChart|Severity'), tdClass: 'border-0' },
    { key: 'chartData', label: '', tdClass: 'border-0 w-100' },
    { key: 'changeInPercent', label: '%', thClass: 'text-right', tdClass: 'border-0 text-right' },
    {
      key: 'currentVulnerabilitiesCount',
      label: '#',
      thClass: 'text-right',
      tdClass: 'border-0 text-right',
    },
  ],
  severityLevels: [
    SEVERITY_LEVELS.critical,
    SEVERITY_LEVELS.high,
    SEVERITY_LEVELS.medium,
    SEVERITY_LEVELS.low,
  ].map((l) => l.toLowerCase()),
  apollo: {
    vulnerabilitiesHistory: {
      query() {
        return this.query;
      },
      variables() {
        return {
          fullPath: this.groupFullPath,
          startDate: formatDate(new Date(this.startDate), ISO_DATE),
          endDate: this.formattedEndDateCursor,
        };
      },
      update(results) {
        return this.processRawData(results);
      },
      error() {
        this.errorLoadingVulnerabilitiesHistory = true;
      },
    },
  },
  computed: {
    startDate() {
      return differenceInMilliseconds(millisecondsPerDay * this.vulnerabilitiesHistoryDayRange);
    },
    endDateCursor() {
      return Date.now();
    },
    formattedEndDateCursor() {
      return formatDate(new Date(this.endDateCursor), ISO_DATE);
    },
    charts() {
      const { severityLevels } = this.$options;

      return severityLevels.map((severityLevel) => {
        const history = Object.entries(this.vulnerabilitiesHistory[severityLevel] || {});
        const chartData = history.length ? history : this.emptyDataSet;
        const [pastCount, currentCount] = firstAndLastY(chartData);
        const changeInPercent = formattedChangeInPercent(pastCount, currentCount);

        return {
          severityLevel,
          chartData,
          changeInPercent,
          currentVulnerabilitiesCount: currentCount,
        };
      });
    },
    dateInfo() {
      return sprintf(s__('VulnerabilityChart|%{formattedStartDate} to today'), {
        formattedStartDate: formatDate(this.startDate, 'mmmm dS'),
      });
    },
    emptyDataSet() {
      const formattedStartDate = formatDate(this.startDate, ISO_DATE);
      const formattedEndDate = formatDate(Date.now(), ISO_DATE);
      return [
        [formattedStartDate, 0],
        [formattedEndDate, 0],
      ];
    },
  },
  watch: {
    startDate() {
      this.$apollo.queries.vulnerabilitiesHistory.refetch();
    },
  },
  methods: {
    setVulnerabilitiesHistoryDayRange(days) {
      this.vulnerabilitiesHistory = {};
      this.vulnerabilitiesHistoryDayRange = days;
    },
    processRawData(results) {
      let { vulnerabilitiesCountByDay } = results;

      if (this.groupFullPath) {
        vulnerabilitiesCountByDay = results.group.vulnerabilitiesCountByDay;
      }

      const vulnerabilitiesData = vulnerabilitiesCountByDay.nodes.reduce(
        (acc, v) => {
          const { date, ...severities } = v;
          Object.keys(severities).forEach((severity) => {
            acc[severity] = acc[severity] || {};
            acc[severity][date] = v[severity];
          }, {});

          return acc;
        },
        { ...this.vulnerabilitiesHistory },
      );

      // backend provide the data not sorted - we need to sort it by day first.
      return Object.keys(vulnerabilitiesData).reduce((acc, severity) => {
        acc[severity] = {};
        Object.keys(vulnerabilitiesData[severity])
          .sort()
          .forEach((day) => {
            acc[severity][day] = vulnerabilitiesData[severity][day];
          }, {});

        return acc;
      }, {});
    },
  },
};
</script>

<template>
  <section class="border rounded p-0">
    <div class="p-3">
      <header id="vulnerability-chart-header">
        <h4 class="my-0">
          {{ __('Vulnerabilities over time') }}
        </h4>
        <p ref="timeInfo" class="text-secondary mt-0 js-vulnerabilities-chart-time-info">
          {{ dateInfo }}
        </p>
      </header>
      <chart-buttons
        :days="$options.days"
        :active-day="vulnerabilitiesHistoryDayRange"
        @click="setVulnerabilitiesHistoryDayRange"
      />
    </div>

    <gl-table
      :fields="$options.fields"
      :items="charts"
      :borderless="true"
      thead-class="thead-white"
      class="js-vulnerabilities-chart-severity-level-breakdown mb-2"
    >
      <template #head(changeInPercent)="{ label }">
        <span v-gl-tooltip :title="__('Difference between start date and now')">{{ label }}</span>
      </template>

      <template #head(currentVulnerabilitiesCount)="{ label }">
        <span v-gl-tooltip :title="__('Current vulnerabilities count')">{{ label }}</span>
      </template>

      <template #cell(severityLevel)="{ value }">
        <severity-badge :ref="`severityBadge${value}`" :severity="value" />
      </template>
      <template #cell(chartData)="{ item }">
        <div class="position-relative h-32-px">
          <gl-sparkline-chart
            :ref="`sparklineChart${item.severityLevel}`"
            :height="32"
            :data="item.chartData"
            :tooltip-label="__('Vulnerabilities')"
            :show-last-y-value="false"
            class="position-absolute w-100 position-top-0 position-left-0"
          />
        </div>
      </template>
      <template #cell(changeInPercent)="{ value }">
        <span ref="changeInPercent">{{ value }}</span>
      </template>
      <template #cell(currentVulnerabilitiesCount)="{ value }">
        <span ref="currentVulnerabilitiesCount">{{ value }}</span>
      </template>
    </gl-table>
  </section>
</template>
