package vulnerability

import (
	"fmt"

	grypeDB "github.com/anchore/grype/grype/db/v5"
	"github.com/anchore/grype/grype/pkg/qualifier"
	"github.com/anchore/grype/grype/version"
	"github.com/anchore/syft/syft/cpe"
)

type Reference struct {
	ID        string
	Namespace string
}

type Vulnerability struct {
	Constraint             version.Constraint
	PackageQualifiers      []qualifier.Qualifier
	CPEs                   []cpe.CPE
	ID                     string
	Namespace              string
	Fix                    Fix
	Advisories             []Advisory
	RelatedVulnerabilities []Reference
}

func NewVulnerability(vuln grypeDB.Vulnerability) (*Vulnerability, error) {
	format := version.ParseFormat(vuln.VersionFormat)

	constraint, err := version.GetConstraint(vuln.VersionConstraint, format)
	if err != nil {
		return nil, fmt.Errorf("failed to parse constraint='%s' format='%s': %w", vuln.VersionConstraint, format, err)
	}

	pkgQualifiers := make([]qualifier.Qualifier, len(vuln.PackageQualifiers))
	for idx, q := range vuln.PackageQualifiers {
		pkgQualifiers[idx] = q.Parse()
	}

	advisories := make([]Advisory, len(vuln.Advisories))
	for idx, advisory := range vuln.Advisories {
		advisories[idx] = Advisory{
			ID:   advisory.ID,
			Link: advisory.Link,
		}
	}

	var relatedVulnerabilities []Reference
	for _, r := range vuln.RelatedVulnerabilities {
		relatedVulnerabilities = append(relatedVulnerabilities, Reference{
			ID:        r.ID,
			Namespace: r.Namespace,
		})
	}

	return &Vulnerability{
		Constraint:        constraint,
		ID:                vuln.ID,
		CPEs:              make([]cpe.CPE, 0),
		Namespace:         vuln.Namespace,
		PackageQualifiers: pkgQualifiers,
		Fix: Fix{
			Versions: vuln.Fix.Versions,
			State:    vuln.Fix.State,
		},
		Advisories:             advisories,
		RelatedVulnerabilities: relatedVulnerabilities,
	}, nil
}

func (v Vulnerability) String() string {
	return fmt.Sprintf("Vuln(id=%s constraint=%q qualifiers=%+v)", v.ID, v.Constraint.String(), v.PackageQualifiers)
}

func (v *Vulnerability) hash() string {
	return fmt.Sprintf("%s|%s|%+v|%+v", v.ID, v.Constraint, v.PackageQualifiers, v.CPEs)
}
