<script>
import { GlFriendlyWrap, GlLink } from '@gitlab/ui';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import SeverityBadge from './severity_badge.vue';
import getFileLocation from '../store/utils/get_file_location';
import VulnerabilityDetail from './vulnerability_detail.vue';
import { s__, sprintf } from '~/locale';
import { REPORT_TYPES } from 'ee/security_dashboard/store/constants';

export default {
  name: 'VulnerabilityDetails',
  components: { CodeBlock, GlFriendlyWrap, SeverityBadge, VulnerabilityDetail, GlLink },
  props: { vulnerability: { type: Object, required: true } },
  computed: {
    url() {
      return this.vulnerability.request?.url || getFileLocation(this.vulnerability.location);
    },
    file() {
      const file = this.vulnerability?.location?.file;
      if (!file) {
        return null;
      }
      let lineSuffix = '';
      const { start_line: startLine, end_line: endLine } = this.vulnerability.location;
      if (startLine) {
        lineSuffix += `:${startLine}`;
        if (endLine && startLine !== endLine) {
          lineSuffix += `-${endLine}`;
        }
      }
      return `${file}${lineSuffix}`;
    },
    identifiers() {
      return this.asNonEmptyListOrNull(this.vulnerability.identifiers);
    },
    crashAddress() {
      return this.vulnerability.location?.crash_address;
    },
    className() {
      return this.vulnerability.location?.class;
    },
    methodName() {
      return this.vulnerability.location?.method;
    },
    image() {
      return this.vulnerability.location?.image;
    },
    namespace() {
      return this.vulnerability.location?.operating_system;
    },
    links() {
      return this.asNonEmptyListOrNull(this.vulnerability.links);
    },
    requestHeaders() {
      return this.headersToFormattedString(this.vulnerability.request?.headers);
    },
    responseHeaders() {
      return this.headersToFormattedString(this.vulnerability.response?.headers);
    },
    responseStatusCode() {
      return this.vulnerability.response?.status_code;
    },
    scannerType() {
      return REPORT_TYPES[this.vulnerability.report_type];
    },
    scannerUrl() {
      return this.vulnerability.scanner?.url || '';
    },
    scannerVersion() {
      return this.vulnerability.scanner?.version;
    },
    scannerName() {
      return this.vulnerability.scanner?.name;
    },
    scannerProvider() {
      if (!this.scannerVersion) {
        return this.scannerName;
      }
      return sprintf(s__('Vulnerability|%{scannerName} (version %{scannerVersion})'), {
        scannerName: this.scannerName,
        scannerVersion: this.scannerVersion,
      });
    },
    stacktraceSnippet() {
      return this.vulnerability.location?.stacktrace_snippet;
    },
  },
  methods: {
    hasMoreValues(index, values) {
      return index < values.length - 1;
    },
    asNonEmptyListOrNull(list) {
      return list?.length > 0 ? list : null;
    },
    headersToFormattedString(headers = []) {
      return headers.map(({ name, value }) => `${name}: ${value}`).join('\n');
    },
  },
};
</script>
<template>
  <div class="border-white mb-0 px-3">
    <vulnerability-detail
      v-if="vulnerability.description"
      :label="s__('Vulnerability|Description')"
    >
      <gl-friendly-wrap :text="vulnerability.description" />
    </vulnerability-detail>
    <vulnerability-detail v-if="vulnerability.project" :label="s__('Vulnerability|Project')">
      <gl-link ref="projectLink" :href="vulnerability.project.full_path" target="_blank">
        <gl-friendly-wrap :text="vulnerability.project.full_name" />
      </gl-link>
    </vulnerability-detail>
    <vulnerability-detail v-if="methodName" :label="s__('Vulnerability|Method')">
      <gl-friendly-wrap :text="methodName" />
    </vulnerability-detail>
    <vulnerability-detail v-if="url" :label="__('URL')">
      <gl-link ref="urlLink" :href="url" target="_blank">
        <gl-friendly-wrap :text="url" />
      </gl-link>
    </vulnerability-detail>
    <vulnerability-detail v-if="requestHeaders" :label="__('Request Headers')">
      <code-block ref="requestHeaders" :code="requestHeaders" max-height="225px" />
    </vulnerability-detail>
    <vulnerability-detail v-if="responseStatusCode" :label="__('Response Status')">
      <gl-friendly-wrap ref="responseStatusCode" :text="responseStatusCode" />
    </vulnerability-detail>
    <vulnerability-detail v-if="responseHeaders" :label="__('Response Headers')">
      <code-block ref="responseHeaders" :code="responseHeaders" max-height="225px" />
    </vulnerability-detail>
    <vulnerability-detail v-if="file" :label="s__('Vulnerability|File')">
      <gl-link
        v-if="vulnerability.blob_path"
        ref="fileLink"
        :href="vulnerability.blob_path"
        target="_blank"
      >
        <gl-friendly-wrap :text="file" />
      </gl-link>
      <gl-friendly-wrap v-else :text="file" />
    </vulnerability-detail>
    <vulnerability-detail v-if="vulnerability.evidence" :label="s__('Vulnerability|Evidence')">
      <gl-friendly-wrap :text="vulnerability.evidence" />
    </vulnerability-detail>
    <vulnerability-detail v-if="crashAddress" :label="s__('Vulnerability|Crash Address')">
      <gl-friendly-wrap ref="crashAddress" :text="crashAddress" />
    </vulnerability-detail>
    <vulnerability-detail v-if="stacktraceSnippet" :label="s__('Stacktrace snippet')">
      <code-block ref="stacktraceSnippet" :code="stacktraceSnippet" max-height="225px" />
    </vulnerability-detail>
    <vulnerability-detail v-if="identifiers" :label="s__('Vulnerability|Identifiers')">
      <span v-for="(identifier, i) in identifiers" :key="i">
        <gl-link
          v-if="identifier.url"
          ref="identifiersLink"
          :href="identifier.url"
          target="_blank"
          rel="nofollow"
        >
          {{ identifier.name }}
        </gl-link>
        <span v-else> {{ identifier.name }} </span>
        <span v-if="hasMoreValues(i, identifiers)">,&nbsp;</span>
      </span>
    </vulnerability-detail>
    <vulnerability-detail v-if="vulnerability.severity" :label="s__('Vulnerability|Severity')">
      <severity-badge :severity="vulnerability.severity" class="d-inline" />
    </vulnerability-detail>
    <vulnerability-detail v-if="vulnerability.report_type" :label="s__('Vulnerability|Scanner')">
      <gl-friendly-wrap :text="scannerType" />
    </vulnerability-detail>
    <vulnerability-detail v-if="scannerProvider" :label="s__('Vulnerability|Scanner Provider')">
      <gl-link v-if="scannerUrl" ref="scannerLink" :href="scannerUrl" target="_blank">
        <gl-friendly-wrap :text="scannerProvider" />
      </gl-link>
      <gl-friendly-wrap v-else :text="scannerProvider" />
    </vulnerability-detail>
    <vulnerability-detail v-if="className" :label="s__('Vulnerability|Class')">
      <gl-friendly-wrap :text="className" />
    </vulnerability-detail>
    <vulnerability-detail v-if="image" :label="s__('Vulnerability|Image')">
      <gl-friendly-wrap :text="image" />
    </vulnerability-detail>
    <vulnerability-detail v-if="namespace" :label="s__('Vulnerability|Namespace')">
      <gl-friendly-wrap :text="namespace" />
    </vulnerability-detail>
    <vulnerability-detail v-if="links" :label="s__('Vulnerability|Links')">
      <span v-for="(link, i) in links" :key="i">
        <gl-link ref="linksLink" :href="link.url" target="_blank">
          {{ link.value || link.url }}
        </gl-link>
        <span v-if="hasMoreValues(i, links)">,&nbsp;</span>
      </span>
    </vulnerability-detail>
  </div>
</template>
