// Package sbom generates Software Bills of Materials in CycloneDX 1.4 JSON format.
// https://cyclonedx.org/specification/overview/
package sbom

import (
	"fmt"
	"time"
)

const (
	FormatCycloneDX = "cyclonedx-json-1.4"
	SpecVersion     = "1.4"
	BOMFormat       = "CycloneDX"
)

// Document is the top-level CycloneDX 1.4 BOM.
type Document struct {
	BOMFormat    string      `json:"bomFormat"`
	SpecVersion  string      `json:"specVersion"`
	SerialNumber string      `json:"serialNumber"`
	Version      int         `json:"version"`
	Metadata     Metadata    `json:"metadata"`
	Components   []Component `json:"components"`
}

type Metadata struct {
	Timestamp string      `json:"timestamp"`
	Tools     []Tool      `json:"tools"`
	Component *Component  `json:"component,omitempty"`
}

type Tool struct {
	Vendor  string `json:"vendor"`
	Name    string `json:"name"`
	Version string `json:"version"`
}

// Component represents a software dependency in the BOM.
type Component struct {
	Type        string       `json:"type"` // "library", "application", "framework"
	Name        string       `json:"name"`
	Version     string       `json:"version,omitempty"`
	PURL        string       `json:"purl,omitempty"`
	Description string       `json:"description,omitempty"`
	Scope       string       `json:"scope,omitempty"` // "required", "optional"
	ExternalRefs []ExternalRef `json:"externalReferences,omitempty"`
}

type ExternalRef struct {
	Type string `json:"type"` // "website", "vcs", "distribution"
	URL  string `json:"url"`
}

// NewDocument creates a blank CycloneDX 1.4 document with metadata populated.
func NewDocument(repoName, sha string) *Document {
	return &Document{
		BOMFormat:    BOMFormat,
		SpecVersion:  SpecVersion,
		SerialNumber: fmt.Sprintf("urn:uuid:forgebucket:%s:%s", repoName, sha[:7]),
		Version:      1,
		Metadata: Metadata{
			Timestamp: time.Now().UTC().Format(time.RFC3339),
			Tools: []Tool{
				{Vendor: "ForgeBucket", Name: "sbom-generator", Version: "1.0.0"},
			},
			Component: &Component{
				Type: "application",
				Name: repoName,
			},
		},
		Components: []Component{},
	}
}

// PURL helpers — produce Package URL strings per ecosystem.

func golangPURL(module, version string) string {
	return fmt.Sprintf("pkg:golang/%s@%s", module, version)
}

func npmPURL(name, version string) string {
	return fmt.Sprintf("pkg:npm/%s@%s", name, version)
}

func pypiPURL(name, version string) string {
	return fmt.Sprintf("pkg:pypi/%s@%s", name, version)
}

func cargoPURL(name, version string) string {
	return fmt.Sprintf("pkg:cargo/%s@%s", name, version)
}

func gemPURL(name, version string) string {
	return fmt.Sprintf("pkg:gem/%s@%s", name, version)
}

func mavenPURL(group, artifact, version string) string {
	return fmt.Sprintf("pkg:maven/%s/%s@%s", group, artifact, version)
}
