package model

import (
	"database/sql"
	"encoding/json"
	"fmt"

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

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;"`
	RecordSource string         `gorm:"primary_key; column:record_source;"`
	Severity     string         `gorm:"column:severity"`
	Links        string         `gorm:"column:links"`
	Description  string         `gorm:"column:description"`
	CvssV2       sql.NullString `gorm:"column:cvss_v2"`
	CvssV3       sql.NullString `gorm:"column:cvss_v3"`
}

// NewVulnerabilityMetadataModel generates a new model from a db.VulnerabilityMetadata struct.
func NewVulnerabilityMetadataModel(metadata v1.VulnerabilityMetadata) VulnerabilityMetadataModel {
	links, err := json.Marshal(metadata.Links)
	if err != nil {
		// TODO: just no
		panic(err)
	}

	var cvssV2Str sql.NullString
	if metadata.CvssV2 != nil {
		cvssV2, err := json.Marshal(*metadata.CvssV2)
		if err != nil {
			// TODO: just no
			panic(err)
		}
		cvssV2Str.String = string(cvssV2)
		cvssV2Str.Valid = true
	}

	var cvssV3Str sql.NullString
	if metadata.CvssV3 != nil {
		cvssV3, err := json.Marshal(*metadata.CvssV3)
		if err != nil {
			// TODO: just no
			panic(err)
		}
		cvssV3Str.String = string(cvssV3)
		cvssV3Str.Valid = true
	}

	return VulnerabilityMetadataModel{
		ID:           metadata.ID,
		RecordSource: metadata.RecordSource,
		Severity:     metadata.Severity,
		Links:        string(links),
		Description:  metadata.Description,
		CvssV2:       cvssV2Str,
		CvssV3:       cvssV3Str,
	}
}

// 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() (v1.VulnerabilityMetadata, error) {
	var links []string
	var cvssV2, cvssV3 *v1.Cvss

	if err := json.Unmarshal([]byte(m.Links), &links); err != nil {
		return v1.VulnerabilityMetadata{}, fmt.Errorf("unable to unmarshal links (%+v): %w", m.Links, err)
	}

	if m.CvssV2.Valid {
		err := json.Unmarshal([]byte(m.CvssV2.String), &cvssV2)
		if err != nil {
			return v1.VulnerabilityMetadata{}, fmt.Errorf("unable to unmarshal cvssV2 data (%+v): %w", m.CvssV2, err)
		}
	}

	if m.CvssV3.Valid {
		err := json.Unmarshal([]byte(m.CvssV3.String), &cvssV3)
		if err != nil {
			return v1.VulnerabilityMetadata{}, fmt.Errorf("unable to unmarshal cvssV3 data (%+v): %w", m.CvssV3, err)
		}
	}

	return v1.VulnerabilityMetadata{
		ID:           m.ID,
		RecordSource: m.RecordSource,
		Severity:     m.Severity,
		Links:        links,
		Description:  m.Description,
		CvssV2:       cvssV2,
		CvssV3:       cvssV3,
	}, nil
}
