package model

import (
	"encoding/json"
	"fmt"

	sqlite "github.com/anchore/grype/grype/db/internal/sqlite"
	v5 "github.com/anchore/grype/grype/db/v5"
)

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"`
}

// NewVulnerabilityMetadataModel generates a new model from a db.VulnerabilityMetadata struct.
func NewVulnerabilityMetadataModel(metadata v5.VulnerabilityMetadata) VulnerabilityMetadataModel {
	if metadata.Cvss == nil {
		metadata.Cvss = make([]v5.Cvss, 0)
	}

	return VulnerabilityMetadataModel{
		ID:           metadata.ID,
		Namespace:    metadata.Namespace,
		DataSource:   metadata.DataSource,
		RecordSource: metadata.RecordSource,
		Severity:     metadata.Severity,
		URLs:         sqlite.ToNullString(metadata.URLs),
		Description:  metadata.Description,
		Cvss:         sqlite.ToNullString(metadata.Cvss),
	}
}

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

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

	if err := json.Unmarshal(m.URLs.ToByteSlice(), &links); err != nil {
		return v5.VulnerabilityMetadata{}, fmt.Errorf("unable to unmarshal URLs (%+v): %w", m.URLs, err)
	}

	err := json.Unmarshal(m.Cvss.ToByteSlice(), &cvss)
	if err != nil {
		return v5.VulnerabilityMetadata{}, fmt.Errorf("unable to unmarshal cvss data (%+v): %w", m.Cvss, err)
	}

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