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
	PkgIdentifier    ftypes.PkgIdentifier `json:",omitzero"`
	InstalledVersion string               `json:",omitempty"`
	FixedVersion     string               `json:",omitempty"`
	Status           types.Status         `json:",omitempty"`
	Layer            ftypes.Layer         `json:",omitzero"`
	SeveritySource   types.SourceID       `json:",omitempty"`
	PrimaryURL       string               `json:",omitempty"`

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

	// Fingerprint is a unique identifier for the vulnerability based on
	// ArtifactID, Target, PkgID, and VulnerabilityID
	Fingerprint string `json:",omitempty"`

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

	// Embed vulnerability details
	types.Vulnerability
}

func (DetectedVulnerability) FindingType() FindingType { return FindingTypeVulnerability }

// 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, ID, severity, vulnerabilityID and package path
func (v BySeverity) Less(i, j int) bool {
	switch {
	case v[i].PkgName != v[j].PkgName:
		return v[i].PkgName < v[j].PkgName
	case v[i].PkgID != v[j].PkgID:
		// For cases where there are different pkgIDs but the same pkgName.
		// e.g. same packages from different maven module.
		return v[i].PkgID < v[j].PkgID
	case 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] }
