//   Copyright 2022 DigitalOcean
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package ceph

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// Version contains all the Ceph version details
type Version struct {
	Major    int
	Minor    int
	Patch    int
	Revision int
	Commit   string
}

var (
	// ErrInvalidVersion indicates that the given version string was invalid
	ErrInvalidVersion = errors.New("invalid version")

	// Nautilus is the *Version at which Ceph nautilus was released
	Nautilus = &Version{Major: 14, Minor: 2, Patch: 0, Revision: 0, Commit: ""}

	// Octopus is the *Version at which Ceph octopus was released
	Octopus = &Version{Major: 15, Minor: 2, Patch: 0, Revision: 0, Commit: ""}

	// Pacific is the *Version at which Ceph pacific was released
	Pacific = &Version{Major: 16, Minor: 2, Patch: 0, Revision: 0, Commit: ""}
)

// IsAtLeast returns true if the version is at least as new as the given constraint
// the commit is not considered
func (version *Version) IsAtLeast(constraint *Version) bool {
	if version.Major > constraint.Major {
		return true
	} else if version.Major < constraint.Major {
		return false
	}

	if version.Minor > constraint.Minor {
		return true
	} else if version.Minor < constraint.Minor {
		return false
	}

	if version.Patch > constraint.Patch {
		return true
	} else if version.Patch < constraint.Patch {
		return false
	}

	if version.Revision > constraint.Revision {
		return true
	} else if version.Revision < constraint.Revision {
		return false
	}

	// the versions must be the same
	return true
}

func (version *Version) String() string {
	str := fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Patch)
	if version.Revision != 0 || version.Commit != "" {
		str = fmt.Sprintf("%s-%d", str, version.Patch)
		if version.Commit != "" {
			str = fmt.Sprintf("%s-%s", str, version.Commit)
		}
	}

	return str
}

// ParseCephVersion parses the given ceph version string to a *Version or error
func ParseCephVersion(cephVersion string) (*Version, error) {
	// standardize ceph-ansible version format
	var re = regexp.MustCompile(`(\d+\.\d+\.\d+-\d+)\.(.*)`)
	cephVersion = re.ReplaceAllString(cephVersion, `$1-$2`)

	splitVersion := strings.Split(cephVersion, " ")
	if len(splitVersion) < 3 {
		return nil, ErrInvalidVersion
	}

	someVersions := strings.Split(splitVersion[2], ".")
	if len(someVersions) != 3 {
		return nil, ErrInvalidVersion
	}

	otherVersions := strings.Split(someVersions[2], "-")
	if len(otherVersions) == 1 {
		otherVersions = []string{otherVersions[0], "0", ""}
	} else if len(otherVersions) != 3 {
		return nil, ErrInvalidVersion
	}

	major, err := strconv.Atoi(someVersions[0])
	if err != nil {
		return nil, err
	}

	minor, err := strconv.Atoi(someVersions[1])
	if err != nil {
		return nil, err
	}

	patch, err := strconv.Atoi(otherVersions[0])
	if err != nil {
		return nil, err
	}

	revision, err := strconv.Atoi(otherVersions[1])
	if err != nil {
		return nil, err
	}

	commit := otherVersions[2]

	return &Version{
		Major:    major,
		Minor:    minor,
		Patch:    patch,
		Revision: revision,
		Commit:   commit,
	}, nil
}
