package pkg

import (
	"sort"

	"github.com/scylladb/go-set/strset"
)

var _ FileOwner = (*PythonPackage)(nil)

// PythonPackage represents all captured data for a python egg or wheel package (specifically as outlined in
// the PyPA core metadata specification https://packaging.python.org/en/latest/specifications/core-metadata/).
// Historically these were defined in PEPs 345, 314, and 241, but have been superseded by PEP 566. This means that this
// struct can (partially) express at least versions 1.0, 1.1, 1.2, 2.1, 2.2, and 2.3 of the metadata format.
type PythonPackage struct {
	// Name is the package name from the Name field in PKG-INFO or METADATA.
	Name string `json:"name" mapstructure:"Name"`
	// Version is the package version from the Version field in PKG-INFO or METADATA.
	Version string `json:"version" mapstructure:"Version"`
	// Author is the package author name from the Author field.
	Author string `json:"author" mapstructure:"Author"`
	// AuthorEmail is the package author's email address from the Author-Email field.
	AuthorEmail string `json:"authorEmail" mapstructure:"AuthorEmail"`
	// Platform indicates the target platform for the package (e.g., "any", "linux", "win32").
	Platform string `json:"platform" mapstructure:"Platform"`
	// Files are the installed files listed in the RECORD file for wheels or installed-files.txt for eggs.
	Files []PythonFileRecord `json:"files,omitempty"`
	// SitePackagesRootPath is the root directory path containing the package (e.g., "/usr/lib/python3.9/site-packages").
	SitePackagesRootPath string `json:"sitePackagesRootPath"`
	// TopLevelPackages are the top-level Python module names from top_level.txt file.
	TopLevelPackages []string `json:"topLevelPackages,omitempty"`
	// DirectURLOrigin contains VCS or direct URL installation information from direct_url.json.
	DirectURLOrigin *PythonDirectURLOriginInfo `json:"directUrlOrigin,omitempty"`
	// RequiresPython specifies the Python version requirement (e.g., ">=3.6").
	RequiresPython string `json:"requiresPython,omitempty" mapstructure:"RequiresPython"`
	// RequiresDist lists the package dependencies with version specifiers from Requires-Dist fields.
	RequiresDist []string `json:"requiresDist,omitempty" mapstructure:"RequiresDist"`
	// ProvidesExtra lists optional feature names that can be installed via extras (e.g., "dev", "test").
	ProvidesExtra []string `json:"providesExtra,omitempty" mapstructure:"ProvidesExtra"`
}

// PythonFileDigest represents the file metadata for a single file attributed to a python package.
type PythonFileDigest struct {
	// Algorithm is the hash algorithm used (e.g., "sha256").
	Algorithm string `json:"algorithm"`
	// Value is the hex-encoded hash digest value.
	Value string `json:"value"`
}

// PythonFileRecord represents a single entry within a RECORD file for a python wheel or egg package
type PythonFileRecord struct {
	// Path is the installed file path from the RECORD file.
	Path string `json:"path"`
	// Digest contains the hash algorithm and value for file integrity verification.
	Digest *PythonFileDigest `json:"digest,omitempty"`
	// Size is the file size in bytes as a string.
	Size string `json:"size,omitempty"`
}

// PythonDirectURLOriginInfo represents installation source metadata from direct_url.json for packages installed from VCS or direct URLs.
type PythonDirectURLOriginInfo struct {
	// URL is the source URL from which the package was installed.
	URL string `json:"url"`
	// CommitID is the VCS commit hash if installed from version control.
	CommitID string `json:"commitId,omitempty"`
	// VCS is the version control system type (e.g., "git", "hg").
	VCS string `json:"vcs,omitempty"`
}

func (m PythonPackage) OwnedFiles() (result []string) {
	s := strset.New()
	for _, f := range m.Files {
		if f.Path != "" {
			s.Add(f.Path)
		}
	}
	result = s.List()
	sort.Strings(result)
	return result
}

// PythonPdmLockExtraVariant represents a specific extras combination variant within a PDM lock file.
// PDM creates separate package entries for different extras combinations; this struct captures those variants.
type PythonPdmLockExtraVariant struct {
	// Extras are the optional extras enabled for this variant (e.g., ["toml"], ["dev"], or ["toml", "dev"])
	Extras []string `json:"extras"`
	// Dependencies are the dependencies specific to this extras variant
	Dependencies []string `json:"dependencies,omitempty"`
	// Files are the package files specific to this variant (only populated if different from base)
	Files []PythonPdmFileEntry `json:"files,omitempty"`
	// Marker is the environment conditional expression for this variant (e.g., "python_version < \"3.11\"")
	Marker string `json:"marker,omitempty"`
}

// PythonPdmLockEntry represents a single package entry within a pdm.lock file.
type PythonPdmLockEntry struct {
	// Summary provides a description of the package
	Summary string `mapstructure:"summary" json:"summary"`
	// Files are the package files with their paths and hash digests (for the base package without extras)
	Files []PythonPdmFileEntry `mapstructure:"files" json:"files"`
	// Marker is the "environment" --conditional expressions that determine whether a package should be installed based on the runtime environment
	Marker string `mapstructure:"marker" json:"marker,omitempty"`
	// RequiresPython specifies the Python version requirement (e.g., ">=3.6").
	RequiresPython string `mapstructure:"RequiresPython" json:"requiresPython,omitempty" `
	// Dependencies are the dependency specifications for the base package (without extras)
	Dependencies []string `mapstructure:"dependencies" json:"dependencies,omitempty"`
	// Extras contains variants for different extras combinations (PDM may have multiple entries per package)
	Extras []PythonPdmLockExtraVariant `json:"extras,omitempty"`
}

type PythonPdmFileEntry struct {
	// URL is the file download URL
	URL string `mapstructure:"url" json:"url"`
	// Digest is the hash digest of the file hosted at the URL
	Digest PythonFileDigest `mapstructure:"hash" json:"digest"`
}

// PythonPipfileLockEntry represents a single package entry within a Pipfile.lock file.
type PythonPipfileLockEntry struct {
	// Hashes are the package file hash values in the format "algorithm:digest" for integrity verification.
	Hashes []string `mapstructure:"hashes" json:"hashes"`
	// Index is the PyPI index name where the package should be fetched from.
	Index string `mapstructure:"index" json:"index"`
}

// PythonPoetryLockEntry represents a single package entry within a Pipfile.lock file.
type PythonPoetryLockEntry struct {
	// Index is the package repository name where the package should be fetched from.
	Index string `mapstructure:"index" json:"index"`
	// Dependencies are the package's runtime dependencies with version constraints.
	Dependencies []PythonPoetryLockDependencyEntry `json:"dependencies"`
	// Extras are optional feature groups that include additional dependencies.
	Extras []PythonPoetryLockExtraEntry `json:"extras,omitempty"`
}

// PythonPoetryLockDependencyEntry represents a single dependency entry within a Poetry lock file.
type PythonPoetryLockDependencyEntry struct {
	// Name is the dependency package name.
	Name string `json:"name"`
	// Version is the locked version or version constraint for the dependency.
	Version string `json:"version"`
	// Optional indicates whether this dependency is optional (only needed for certain extras).
	Optional bool `json:"optional"`
	// Markers are environment marker expressions that conditionally enable the dependency (e.g., "python_version >= '3.8'").
	Markers string `json:"markers,omitempty"`
	// Extras are the optional feature names from the dependency that should be installed.
	Extras []string `json:"extras,omitempty"`
}

// PythonPoetryLockExtraEntry represents an optional feature group in a Poetry lock file.
type PythonPoetryLockExtraEntry struct {
	// Name is the optional feature name (e.g., "dev", "test").
	Name string `json:"name"`
	// Dependencies are the package names required when this extra is installed.
	Dependencies []string `json:"dependencies"`
}

// PythonRequirementsEntry represents a single entry within a [*-]requirements.txt file.
type PythonRequirementsEntry struct {
	// Name is the package name from the requirements file.
	Name string `json:"name" mapstructure:"Name"`
	// Extras are the optional features to install from the package (e.g., package[dev,test]).
	Extras []string `json:"extras,omitempty" mapstructure:"Extras"`
	// VersionConstraint specifies version requirements (e.g., ">=1.0,<2.0").
	VersionConstraint string `json:"versionConstraint" mapstructure:"VersionConstraint"`
	// URL is the direct download URL or VCS URL if specified instead of a PyPI package.
	URL string `json:"url,omitempty" mapstructure:"URL"`
	// Markers are environment marker expressions for conditional installation (e.g., "python_version >= '3.8'").
	Markers string `json:"markers,omitempty" mapstructure:"Markers"`
}

// PythonUvLockDependencyEntry represents a single dependency entry within a uv lock file.
type PythonUvLockDependencyEntry struct {
	// Name is the dependency package name.
	Name string `json:"name"`
	// Optional indicates whether this dependency is optional (only needed for certain extras).
	Optional bool `json:"optional"`
	// Markers are environment marker expressions that conditionally enable the dependency (e.g., "python_version >= '3.8'").
	Markers string `json:"markers,omitempty"`
	// Extras are the optional feature names from the dependency that should be installed.
	Extras []string `json:"extras,omitempty"`
}

// PythonUvLockExtraEntry represents an optional feature group in a uv lock file.
type PythonUvLockExtraEntry struct {
	// Name is the optional feature name (e.g., "dev", "test").
	Name string `json:"name"`
	// Dependencies are the package names required when this extra is installed.
	Dependencies []string `json:"dependencies"`
}

// PythonUvLockEntry represents a single package entry within a uv.lock file.
type PythonUvLockEntry struct {
	// Index is the package repository name where the package should be fetched from.
	Index string `mapstructure:"index" json:"index"`
	// Dependencies are the package's runtime dependencies with version constraints.
	Dependencies []PythonUvLockDependencyEntry `json:"dependencies"`
	// Extras are optional feature groups that include additional dependencies.
	Extras []PythonUvLockExtraEntry `json:"extras,omitempty"`
}
