package models

import (
	"time"

	"github.com/anchore/grype/grype/vulnerability"
)

type VulnerabilityMetadata struct {
	ID             string           `json:"id"`
	DataSource     string           `json:"dataSource"`
	Namespace      string           `json:"namespace,omitempty"`
	Severity       string           `json:"severity,omitempty"`
	URLs           []string         `json:"urls"`
	Description    string           `json:"description,omitempty"`
	Cvss           []Cvss           `json:"cvss"`
	KnownExploited []KnownExploited `json:"knownExploited,omitempty"`
	EPSS           []EPSS           `json:"epss,omitempty"`
	CWEs           []CWE            `json:"cwes,omitempty"`
}

type KnownExploited struct {
	CVE                        string   `json:"cve"`
	VendorProject              string   `json:"vendorProject,omitempty"`
	Product                    string   `json:"product,omitempty"`
	DateAdded                  string   `json:"dateAdded,omitempty"`
	RequiredAction             string   `json:"requiredAction,omitempty"`
	DueDate                    string   `json:"dueDate,omitempty"`
	KnownRansomwareCampaignUse string   `json:"knownRansomwareCampaignUse"`
	Notes                      string   `json:"notes,omitempty"`
	URLs                       []string `json:"urls,omitempty"`
	CWEs                       []string `json:"cwes,omitempty"`
}

type EPSS struct {
	CVE        string  `json:"cve"`
	EPSS       float64 `json:"epss"`
	Percentile float64 `json:"percentile"`
	Date       string  `json:"date"`
}

type CWE struct {
	Cve    string `json:"cve"`
	CWE    string `json:"cwe,omitempty"`
	Source string `json:"source,omitempty"`
	Type   string `json:"type,omitempty"`
}

func NewVulnerabilityMetadata(id, namespace string, metadata *vulnerability.Metadata) VulnerabilityMetadata {
	if metadata == nil {
		return VulnerabilityMetadata{
			ID:        id,
			Namespace: namespace,
		}
	}

	urls := metadata.URLs
	if urls == nil {
		urls = make([]string, 0)
	}

	return VulnerabilityMetadata{
		ID:             id,
		DataSource:     metadata.DataSource,
		Namespace:      metadata.Namespace,
		Severity:       metadata.Severity,
		URLs:           urls,
		Description:    metadata.Description,
		Cvss:           toCVSS(metadata),
		KnownExploited: toKnownExploited(metadata.KnownExploited),
		EPSS:           toEPSS(metadata.EPSS),
		CWEs:           toCWE(metadata.CWEs),
	}
}

func toKnownExploited(knownExploited []vulnerability.KnownExploited) []KnownExploited {
	result := make([]KnownExploited, len(knownExploited))
	for idx, ke := range knownExploited {
		result[idx] = KnownExploited{
			CVE:                        ke.CVE,
			VendorProject:              ke.VendorProject,
			Product:                    ke.Product,
			DateAdded:                  formatDate(ke.DateAdded),
			RequiredAction:             ke.RequiredAction,
			DueDate:                    formatDate(ke.DueDate),
			KnownRansomwareCampaignUse: ke.KnownRansomwareCampaignUse,
			Notes:                      ke.Notes,
			URLs:                       ke.URLs,
			CWEs:                       ke.CWEs,
		}
	}
	return result
}

func formatDate(t *time.Time) string {
	if t == nil {
		return ""
	}
	return t.Format(time.DateOnly)
}

func toEPSS(epss []vulnerability.EPSS) []EPSS {
	result := make([]EPSS, len(epss))
	for idx, e := range epss {
		result[idx] = EPSS{
			CVE:        e.CVE,
			EPSS:       e.EPSS,
			Percentile: e.Percentile,
			Date:       e.Date.Format(time.DateOnly),
		}
	}
	return result
}

func toCWE(cwes []vulnerability.CWE) []CWE {
	result := make([]CWE, len(cwes))
	for idx, e := range cwes {
		result[idx] = CWE{
			Cve:    e.CVE,
			CWE:    e.CWE,
			Source: e.Source,
			Type:   e.Type,
		}
	}
	return result
}
