package handlers

import (
	"fmt"
	"strconv"
	"strings"
)

// Semver represents a parsed semantic version (major.minor.patch[-prerelease]).
type Semver struct {
	Major      int
	Minor      int
	Patch      int
	Prerelease string
}

// ParseSemver parses a version string like "1.2.3" or "v1.2.3-beta.1".
// Leading "v" is stripped. Prerelease suffix after "-" is preserved.
func ParseSemver(s string) (Semver, error) {
	s = strings.TrimSpace(s)
	s = strings.TrimPrefix(s, "v")
	if s == "" {
		return Semver{}, fmt.Errorf("empty version string")
	}

	// Split off prerelease suffix
	var pre string
	if idx := strings.IndexByte(s, '-'); idx >= 0 {
		pre = s[idx+1:]
		s = s[:idx]
	}

	parts := strings.Split(s, ".")
	if len(parts) != 3 {
		return Semver{}, fmt.Errorf("version %q must have exactly 3 parts (major.minor.patch)", s)
	}

	major, err := strconv.Atoi(parts[0])
	if err != nil {
		return Semver{}, fmt.Errorf("invalid major version %q: %w", parts[0], err)
	}
	minor, err := strconv.Atoi(parts[1])
	if err != nil {
		return Semver{}, fmt.Errorf("invalid minor version %q: %w", parts[1], err)
	}
	patch, err := strconv.Atoi(parts[2])
	if err != nil {
		return Semver{}, fmt.Errorf("invalid patch version %q: %w", parts[2], err)
	}

	return Semver{Major: major, Minor: minor, Patch: patch, Prerelease: pre}, nil
}

// Compare returns -1 if a < b, 0 if a == b, +1 if a > b.
// Prerelease versions sort lower than the same release version (per semver spec).
func (a Semver) Compare(b Semver) int {
	if a.Major != b.Major {
		return cmpInt(a.Major, b.Major)
	}
	if a.Minor != b.Minor {
		return cmpInt(a.Minor, b.Minor)
	}
	if a.Patch != b.Patch {
		return cmpInt(a.Patch, b.Patch)
	}
	// Same numeric version — prerelease < release
	if a.Prerelease == b.Prerelease {
		return 0
	}
	if a.Prerelease == "" {
		return 1 // a is release, b has prerelease → a > b
	}
	if b.Prerelease == "" {
		return -1 // a has prerelease, b is release → a < b
	}
	// Both have prerelease — compare lexicographically
	if a.Prerelease < b.Prerelease {
		return -1
	}
	return 1
}

// String returns the version in "major.minor.patch[-prerelease]" format.
func (v Semver) String() string {
	s := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
	if v.Prerelease != "" {
		s += "-" + v.Prerelease
	}
	return s
}

func cmpInt(a, b int) int {
	if a < b {
		return -1
	}
	return 1
}
