package packstate

import (
	"regexp"
	"strings"
)

var semVerRe = regexp.MustCompile(`^([0-9]+)\.([0-9]+)\.([0-9]+)$`)

func isValidSemVer(v string) bool {
	return semVerRe.MatchString(strings.TrimSpace(v))
}

func compareSemVer(a, b string) int {
	ap := parseSemVerParts(a)
	bp := parseSemVerParts(b)
	for i := 0; i < 3; i++ {
		if ap[i] < bp[i] {
			return -1
		}
		if ap[i] > bp[i] {
			return 1
		}
	}
	return 0
}

func parseSemVerParts(v string) [3]int {
	var out [3]int
	m := semVerRe.FindStringSubmatch(strings.TrimSpace(v))
	for i := 1; i <= 3; i++ {
		for _, r := range m[i] {
			out[i-1] = out[i-1]*10 + int(r-'0')
		}
	}
	return out
}

var versionConstraintTermRe = regexp.MustCompile(`^(>=|<=|>|<|=|==)?([0-9]+\.[0-9]+\.[0-9]+)$`)

func VersionSatisfiesConstraint(version, constraint string) bool {
	version = strings.TrimSpace(version)
	constraint = strings.TrimSpace(constraint)
	if !isValidSemVer(version) {
		return false
	}
	if constraint == "" {
		return true
	}
	terms := strings.Fields(constraint)
	if len(terms) == 1 && isValidSemVer(terms[0]) {
		return compareSemVer(version, terms[0]) == 0
	}
	for _, term := range terms {
		m := versionConstraintTermRe.FindStringSubmatch(term)
		if m == nil {
			return false
		}
		op := m[1]
		if op == "" {
			op = "="
		}
		cmp := compareSemVer(version, m[2])
		switch op {
		case "=", "==":
			if cmp != 0 {
				return false
			}
		case ">":
			if cmp <= 0 {
				return false
			}
		case ">=":
			if cmp < 0 {
				return false
			}
		case "<":
			if cmp >= 0 {
				return false
			}
		case "<=":
			if cmp > 0 {
				return false
			}
		default:
			return false
		}
	}
	return true
}
