package version

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/pkg/errors"
	"github.com/rclone/rclone/fs"
)

// Version represents a parsed rclone version number
type Version []int

var parseVersion = regexp.MustCompile(`^(?:rclone )?v(\d+)\.(\d+)(?:\.(\d+))?(?:-(\d+)(?:-(g[\wβ-]+))?)?$`)

// New parses a version number from a string
//
// This will be returned with up to 4 elements for major, minor,
// patch, subpatch release.
//
// If the version number represents a compiled from git version
// number, then it will be returned as major, minor, 999, 999
func New(in string) (v Version, err error) {
	isGit := strings.HasSuffix(in, "-DEV")
	if isGit {
		in = in[:len(in)-4]
	}
	r := parseVersion.FindStringSubmatch(in)
	if r == nil {
		return v, errors.Errorf("failed to match version string %q", in)
	}
	atoi := func(s string) int {
		i, err := strconv.Atoi(s)
		if err != nil {
			fs.Errorf(nil, "Failed to parse %q as int from %q: %v", s, in, err)
		}
		return i
	}
	v = Version{
		atoi(r[1]), // major
		atoi(r[2]), // minor
	}
	if r[3] != "" {
		v = append(v, atoi(r[3])) // patch
	} else if r[4] != "" {
		v = append(v, 0) // patch
	}
	if r[4] != "" {
		v = append(v, atoi(r[4])) // dev
	}
	if isGit {
		v = append(v, 999, 999)
	}
	return v, nil
}

// String converts v to a string
func (v Version) String() string {
	var out []string
	for _, vv := range v {
		out = append(out, fmt.Sprint(vv))
	}
	return strings.Join(out, ".")
}

// Cmp compares two versions returning >0, <0 or 0
func (v Version) Cmp(o Version) (d int) {
	n := len(v)
	if n > len(o) {
		n = len(o)
	}
	for i := 0; i < n; i++ {
		d = v[i] - o[i]
		if d != 0 {
			return d
		}
	}
	return len(v) - len(o)
}

// IsGit returns true if the current version was compiled from git
func (v Version) IsGit() bool {
	return len(v) >= 4 && v[2] == 999 && v[3] == 999
}
