package model

import (
	"encoding/json"
	"fmt"

	v1 "github.com/anchore/grype/grype/db/v1"
)

const (
	VulnerabilityTableName    = "vulnerability"
	GetVulnerabilityIndexName = "get_vulnerability_index"
)

// VulnerabilityModel is a struct used to serialize db.Vulnerability information into a sqlite3 DB.
type VulnerabilityModel struct {
	PK                   uint64 `gorm:"primary_key;auto_increment;"`
	ID                   string `gorm:"column:id"`
	RecordSource         string `gorm:"column:record_source"`
	PackageName          string `gorm:"column:package_name; index:get_vulnerability_index"`
	Namespace            string `gorm:"column:namespace; index:get_vulnerability_index"`
	VersionConstraint    string `gorm:"column:version_constraint"`
	VersionFormat        string `gorm:"column:version_format"`
	CPEs                 string `gorm:"column:cpes"`
	ProxyVulnerabilities string `gorm:"column:proxy_vulnerabilities"`
	FixedInVersion       string `gorm:"column:fixed_in_version"`
}

// NewVulnerabilityModel generates a new model from a db.Vulnerability struct.
func NewVulnerabilityModel(vulnerability v1.Vulnerability) VulnerabilityModel {
	cpes, err := json.Marshal(vulnerability.CPEs)
	if err != nil {
		// TODO: just no
		panic(err)
	}

	proxy, err := json.Marshal(vulnerability.ProxyVulnerabilities)
	if err != nil {
		// TODO: just no
		panic(err)
	}

	return VulnerabilityModel{
		ID:                   vulnerability.ID,
		PackageName:          vulnerability.PackageName,
		RecordSource:         vulnerability.RecordSource,
		Namespace:            vulnerability.Namespace,
		VersionConstraint:    vulnerability.VersionConstraint,
		VersionFormat:        vulnerability.VersionFormat,
		FixedInVersion:       vulnerability.FixedInVersion,
		CPEs:                 string(cpes),
		ProxyVulnerabilities: string(proxy),
	}
}

// TableName returns the table which all db.Vulnerability model instances are stored into.
func (VulnerabilityModel) TableName() string {
	return VulnerabilityTableName
}

// Inflate generates a db.Vulnerability object from the serialized model instance.
func (m *VulnerabilityModel) Inflate() (v1.Vulnerability, error) {
	var cpes []string
	err := json.Unmarshal([]byte(m.CPEs), &cpes)
	if err != nil {
		return v1.Vulnerability{}, fmt.Errorf("unable to unmarshal CPEs (%+v): %w", m.CPEs, err)
	}

	var proxy []string
	err = json.Unmarshal([]byte(m.ProxyVulnerabilities), &proxy)
	if err != nil {
		return v1.Vulnerability{}, fmt.Errorf("unable to unmarshal proxy vulnerabilities (%+v): %w", m.ProxyVulnerabilities, err)
	}

	return v1.Vulnerability{
		ID:                   m.ID,
		RecordSource:         m.RecordSource,
		PackageName:          m.PackageName,
		Namespace:            m.Namespace,
		VersionConstraint:    m.VersionConstraint,
		VersionFormat:        m.VersionFormat,
		CPEs:                 cpes,
		ProxyVulnerabilities: proxy,
		FixedInVersion:       m.FixedInVersion,
	}, nil
}
