package types

import (
	"github.com/aquasecurity/trivy-db/pkg/types"
	ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)

// DetectedVulnerability holds the information of detected vulnerabilities
type DetectedVulnerability struct {
	VulnerabilityID  string         `json:",omitempty"`
	VendorIDs        []string       `json:",omitempty"`
	PkgID            string         `json:",omitempty"` // It is used to construct dependency graph.
	PkgName          string         `json:",omitempty"`
	PkgPath          string         `json:",omitempty"` // This field is populated in the case of language-specific packages such as egg/wheel and gemspec
	InstalledVersion string         `json:",omitempty"`
	FixedVersion     string         `json:",omitempty"`
	Status           types.Status   `json:",omitempty"`
	Layer            ftypes.Layer   `json:",omitempty"`
	SeveritySource   types.SourceID `json:",omitempty"`
	PrimaryURL       string         `json:",omitempty"`

	// PkgRef is populated only when scanning SBOM and contains the reference ID used in the SBOM.
	// It could be PURL, UUID, etc.
	// e.g.
	//    - pkg:npm/acme/component@1.0.0
	//    - b2a46a4b-8367-4bae-9820-95557cfe03a8
	PkgRef string `json:",omitempty"`

	// DataSource holds where the advisory comes from
	DataSource *types.DataSource `json:",omitempty"`

	// Custom is for extensibility and not supposed to be used in OSS
	Custom interface{} `json:",omitempty"`

	// Embed vulnerability details
	types.Vulnerability
}

// GetID retrun Vulnerability ID
func (vuln *DetectedVulnerability) GetID() string {
	return vuln.VulnerabilityID
}

// BySeverity implements sort.Interface based on the Severity field.
type BySeverity []DetectedVulnerability

// Len returns the length of DetectedVulnerabilities
func (v BySeverity) Len() int { return len(v) }

// Less compares 2 DetectedVulnerabilities based on package name, severity, vulnerabilityID and package path
func (v BySeverity) Less(i, j int) bool {
	if v[i].PkgName != v[j].PkgName {
		return v[i].PkgName < v[j].PkgName
	} else if v[i].InstalledVersion != v[j].InstalledVersion {
		return v[i].InstalledVersion < v[j].InstalledVersion
	}
	ret := types.CompareSeverityString(
		v[j].Severity, v[i].Severity,
	)
	if ret != 0 {
		return ret > 0
	}
	if v[i].VulnerabilityID != v[j].VulnerabilityID {
		return v[i].VulnerabilityID < v[j].VulnerabilityID
	}
	return v[i].PkgPath < v[j].PkgPath
}

// Swap swaps 2 vulnerability
func (v BySeverity) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
