package vulnerabilitydatabase

import (
	"encoding/json"

	"github.com/deepfence/ThreatMapper/deepfence_utils/log"
	"github.com/deepfence/ThreatMapper/deepfence_utils/sqlite"
)

const (
	VulnerabilityMetadataTableName = "vulnerability_metadata"
)

// VulnerabilityMetadataModel is a struct used to serialize db.VulnerabilityMetadata information into a sqlite3 DB.
type VulnerabilityMetadataModel struct {
	ID           string            `gorm:"primary_key; column:id;"`
	Namespace    string            `gorm:"primary_key; column:namespace;"`
	DataSource   string            `gorm:"column:data_source"`
	RecordSource string            `gorm:"column:record_source"`
	Severity     string            `gorm:"column:severity"`
	URLs         sqlite.NullString `gorm:"column:urls; default:null"`
	Description  string            `gorm:"column:description"`
	Cvss         sqlite.NullString `gorm:"column:cvss; default:null"`
	CISAKEV      bool              `gorm:"column:cisakev"`
	EPSSScore    float64           `gorm:"column:epss"`
}

// TableName returns the table which all db.VulnerabilityMetadata model instances are stored into.
func (m *VulnerabilityMetadataModel) TableName() string {
	return VulnerabilityMetadataTableName
}

// Inflate generates a db.VulnerabilityMetadataModel object from the serialized model instance.
func (m *VulnerabilityMetadataModel) Inflate() (VulnerabilityMetadata, error) {
	var links []string
	var cvss []Cvss

	if err := json.Unmarshal(m.URLs.ToByteSlice(), &links); err != nil {
		log.Debug().Msgf("unable to unmarshal URLs (%+v): %v", m.URLs, err)
	}

	err := json.Unmarshal(m.Cvss.ToByteSlice(), &cvss)
	if err != nil {
		log.Debug().Msgf("unable to unmarshal cvss data (%+v): %v", m.Cvss, err)
	}

	return VulnerabilityMetadata{
		ID:           m.ID,
		Namespace:    m.Namespace,
		DataSource:   m.DataSource,
		RecordSource: m.RecordSource,
		Severity:     m.Severity,
		URLs:         links,
		Description:  m.Description,
		Cvss:         cvss,
		CISAKEV:      m.CISAKEV,
		EPSSScore:    m.EPSSScore,
	}, nil
}

type VulnerabilityMetadata struct {
	ID           string
	Namespace    string
	DataSource   string
	RecordSource string
	Severity     string
	URLs         []string
	Description  string
	Cvss         []Cvss
	CISAKEV      bool
	EPSSScore    float64
}

// Cvss contains select Common Vulnerability Scoring System fields for a vulnerability.
type Cvss struct {
	// VendorMetadata captures non-standard CVSS fields that vendors can sometimes
	// include when providing CVSS information.  This vendor-specific metadata type
	// allows to capture that data for persisting into the database
	VendorMetadata interface{} `json:"vendor_metadata"`
	Metrics        CvssMetrics `json:"metrics"`
	Vector         string      `json:"vector"`  // A textual representation of the metric values used to determine the score
	Version        string      `json:"version"` // The version of the CVSS spec, for example 2.0, 3.0, or 3.1
	Source         string      `json:"source"`  // Identifies the organization that provided the score
	Type           string      `json:"type"`    // Whether the source is a `primary` or `secondary` source
}

// CvssMetrics are the quantitative values that make up a CVSS score.
type CvssMetrics struct {
	// BaseScore ranges from 0 - 10 and defines qualities intrinsic to the severity of a vulnerability.
	BaseScore float64 `json:"base_score"`
	// ExploitabilityScore is a pointer to avoid having a 0 value by default.
	// It is an indicator of how easy it may be for an attacker to exploit
	// a vulnerability
	ExploitabilityScore *float64 `json:"exploitability_score"`
	// ImpactScore represents the effects of an exploited vulnerability
	// relative to compromise in confidentiality, integrity, and availability.
	// It is an optional parameter, so that is why it is a pointer instead of
	// a regular field
	ImpactScore *float64 `json:"impact_score"`
}
