package r

import (
	"context"
	"strings"

	"github.com/anchore/packageurl-go"
	"github.com/anchore/syft/syft/file"
	"github.com/anchore/syft/syft/pkg"
)

func newPackage(ctx context.Context, pd parseData, locations ...file.Location) pkg.Package {
	locationSet := file.NewLocationSet()
	for _, loc := range locations {
		locationSet.Add(loc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
	}

	licenses := parseLicenseData(ctx, pd.License)

	result := pkg.Package{
		Name:      pd.Package,
		Version:   pd.Version,
		Locations: locationSet,
		Licenses:  pkg.NewLicenseSet(licenses...),
		Language:  pkg.R,
		Type:      pkg.Rpkg,
		PURL:      packageURL(pd),
		Metadata:  pd.RDescription,
	}

	result.SetID()
	return result
}

func packageURL(m parseData) string {
	return packageurl.NewPackageURL("cran", "", m.Package, m.Version, nil, "").ToString()
}

// https://r-pkgs.org/description.html#the-license-field
// four forms:
// 1. "GPL (>= 2)"
// 2. "GPL-2"
// 3. "MIT + file LICENSE"
// 4. "pointer to the full text of the license; file LICENSE"
// Multiple licences can be specified separated by ‘|’
// (surrounded by spaces) in which case the user can choose any of the above cases.
// https://cran.rstudio.com/doc/manuals/r-devel/R-exts.html#Licensing
func parseLicenseData(ctx context.Context, license string, locations ...file.Location) []pkg.License {
	licenses := make([]pkg.License, 0)

	// check if multiple licenses are separated by |
	splitField := strings.Split(license, "|")
	for _, l := range splitField {
		// check case 1 for surrounding parens
		l = strings.TrimSpace(l)
		if strings.Contains(l, "(") && strings.Contains(l, ")") {
			licenseVersion := strings.SplitN(l, " ", 2)
			if len(licenseVersion) == 2 {
				l = strings.Join([]string{licenseVersion[0], parseVersion(licenseVersion[1])}, "")
				licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, l, locations...))
				continue
			}
		}

		// case 3
		if strings.Contains(l, "+") && strings.Contains(l, "LICENSE") {
			splitField := strings.Split(l, " ")
			if len(splitField) > 0 {
				licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, splitField[0], locations...))
				continue
			}
		}

		// TODO: case 4 if we are able to read the location data and find the adjacent file?
		if l == "file LICENSE" {
			continue
		}

		// no specific case found for the above so assume case 2
		// check if the common name in case 2 is valid SDPX otherwise value will be populated
		licenses = append(licenses, pkg.NewLicenseFromLocationsWithContext(ctx, l, locations...))
		continue
	}
	return licenses
}

// attempt to make best guess at SPDX license ID from version operator in case 2
/*
‘<’, ‘<=’, ‘>’, ‘>=’, ‘==’, or ‘!=’
cant be (>= 2.0) OR (>= 2.0, < 3)
since there is no way in SPDX licenses to express < some other version
we attempt to check the constraint to see if this should be + or not
*/
func parseVersion(version string) string {
	version = strings.ReplaceAll(version, "(", "")
	version = strings.ReplaceAll(version, ")", "")

	// multiple constraints
	if strings.Contains(version, ",") {
		multipleConstraints := strings.Split(version, ",")
		// SPDX does not support considering multiple constraints
		// so we will just take the first one and attempt to form the best SPDX ID we can
		for _, v := range multipleConstraints {
			constraintVersion := strings.SplitN(v, " ", 2)
			if len(constraintVersion) == 2 {
				// switch on the operator and return the version with + or without
				switch constraintVersion[0] {
				case ">", ">=":
					return constraintVersion[1] + "+"
				default:
					return constraintVersion[1]
				}
			}
		}
	}
	// single constraint
	singleContraint := strings.Split(version, " ")
	if len(singleContraint) == 2 {
		switch singleContraint[0] {
		case ">", ">=":
			return singleContraint[1] + "+"
		default:
			return singleContraint[1]
		}
	}

	// could not parse version constraint so return ""
	return ""
}
