package versioning

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

func Normalize(version string) string {
	version = strings.TrimSpace(version)
	return strings.TrimPrefix(version, "v")
}

// NormalizeDaemonVersion strips the first-party daemon family prefix and optional
// v prefix before semver comparison.
func NormalizeDaemonVersion(version string) (string, error) {
	trimmed := strings.TrimSpace(version)
	if strings.HasPrefix(strings.ToLower(trimmed), "agentteam/") {
		trimmed = strings.TrimSpace(trimmed[len("agentteam/"):])
	}
	if len(trimmed) > 0 && (trimmed[0] == 'v' || trimmed[0] == 'V') {
		trimmed = trimmed[1:]
	}
	if trimmed == "" {
		return "", fmt.Errorf("daemon version is empty")
	}
	return trimmed, nil
}

// CompareSemver compares two AgentTeam daemon versions using SemVer 2.0.0 ordering.
// It accepts the daemon wire family prefix (agentteam/) and an optional leading v.
func CompareSemver(left string, right string) (int, error) {
	leftVersion, err := parseSemver(left)
	if err != nil {
		return 0, fmt.Errorf("left version: %w", err)
	}
	rightVersion, err := parseSemver(right)
	if err != nil {
		return 0, fmt.Errorf("right version: %w", err)
	}
	return leftVersion.compare(rightVersion), nil
}

type semverVersion struct {
	major int64
	minor int64
	patch int64
	pre   []semverPrereleaseIdentifier
}

type semverPrereleaseIdentifier struct {
	raw     string
	numeric bool
	number  int64
}

func parseSemver(raw string) (semverVersion, error) {
	normalized, err := NormalizeDaemonVersion(raw)
	if err != nil {
		return semverVersion{}, err
	}
	buildSplit := strings.Split(normalized, "+")
	if len(buildSplit) > 2 {
		return semverVersion{}, fmt.Errorf("invalid build metadata")
	}
	if len(buildSplit) == 2 {
		if err := validateDotIdentifiers(buildSplit[1], false); err != nil {
			return semverVersion{}, fmt.Errorf("invalid build metadata: %w", err)
		}
	}

	versionAndPre := strings.SplitN(buildSplit[0], "-", 2)
	core := strings.Split(versionAndPre[0], ".")
	if len(core) != 3 {
		return semverVersion{}, fmt.Errorf("version must be major.minor.patch")
	}
	major, err := parseSemverNumber(core[0])
	if err != nil {
		return semverVersion{}, fmt.Errorf("invalid major: %w", err)
	}
	minor, err := parseSemverNumber(core[1])
	if err != nil {
		return semverVersion{}, fmt.Errorf("invalid minor: %w", err)
	}
	patch, err := parseSemverNumber(core[2])
	if err != nil {
		return semverVersion{}, fmt.Errorf("invalid patch: %w", err)
	}

	parsed := semverVersion{major: major, minor: minor, patch: patch}
	if len(versionAndPre) == 2 {
		pre, err := parsePrerelease(versionAndPre[1])
		if err != nil {
			return semverVersion{}, err
		}
		parsed.pre = pre
	}
	return parsed, nil
}

func parseSemverNumber(raw string) (int64, error) {
	if raw == "" {
		return 0, fmt.Errorf("empty numeric identifier")
	}
	if len(raw) > 1 && raw[0] == '0' {
		return 0, fmt.Errorf("numeric identifier has leading zero")
	}
	for _, ch := range raw {
		if ch < '0' || ch > '9' {
			return 0, fmt.Errorf("numeric identifier must contain only digits")
		}
	}
	value, err := strconv.ParseInt(raw, 10, 64)
	if err != nil {
		return 0, err
	}
	return value, nil
}

func parsePrerelease(raw string) ([]semverPrereleaseIdentifier, error) {
	if raw == "" {
		return nil, fmt.Errorf("invalid prerelease: empty")
	}
	parts := strings.Split(raw, ".")
	identifiers := make([]semverPrereleaseIdentifier, 0, len(parts))
	for _, part := range parts {
		if part == "" {
			return nil, fmt.Errorf("invalid prerelease: empty identifier")
		}
		if err := validateIdentifierChars(part); err != nil {
			return nil, fmt.Errorf("invalid prerelease: %w", err)
		}
		identifier := semverPrereleaseIdentifier{raw: part}
		if isDigits(part) {
			value, err := parseSemverNumber(part)
			if err != nil {
				return nil, fmt.Errorf("invalid prerelease: %w", err)
			}
			identifier.numeric = true
			identifier.number = value
		}
		identifiers = append(identifiers, identifier)
	}
	return identifiers, nil
}

func validateDotIdentifiers(raw string, numericLeadingZeroRestricted bool) error {
	if raw == "" {
		return fmt.Errorf("empty")
	}
	for _, part := range strings.Split(raw, ".") {
		if part == "" {
			return fmt.Errorf("empty identifier")
		}
		if err := validateIdentifierChars(part); err != nil {
			return err
		}
		if numericLeadingZeroRestricted && isDigits(part) {
			if _, err := parseSemverNumber(part); err != nil {
				return err
			}
		}
	}
	return nil
}

func validateIdentifierChars(raw string) error {
	for _, ch := range raw {
		if (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '-' {
			continue
		}
		return fmt.Errorf("identifier contains invalid character %q", ch)
	}
	return nil
}

func isDigits(raw string) bool {
	if raw == "" {
		return false
	}
	for _, ch := range raw {
		if ch < '0' || ch > '9' {
			return false
		}
	}
	return true
}

func (version semverVersion) compare(other semverVersion) int {
	for _, pair := range [][2]int64{
		{version.major, other.major},
		{version.minor, other.minor},
		{version.patch, other.patch},
	} {
		if pair[0] < pair[1] {
			return -1
		}
		if pair[0] > pair[1] {
			return 1
		}
	}
	if len(version.pre) == 0 && len(other.pre) == 0 {
		return 0
	}
	if len(version.pre) == 0 {
		return 1
	}
	if len(other.pre) == 0 {
		return -1
	}
	for index := 0; index < len(version.pre) && index < len(other.pre); index++ {
		cmp := comparePrereleaseIdentifier(version.pre[index], other.pre[index])
		if cmp != 0 {
			return cmp
		}
	}
	if len(version.pre) < len(other.pre) {
		return -1
	}
	if len(version.pre) > len(other.pre) {
		return 1
	}
	return 0
}

func comparePrereleaseIdentifier(left semverPrereleaseIdentifier, right semverPrereleaseIdentifier) int {
	if left.numeric && right.numeric {
		if left.number < right.number {
			return -1
		}
		if left.number > right.number {
			return 1
		}
		return 0
	}
	if left.numeric {
		return -1
	}
	if right.numeric {
		return 1
	}
	return strings.Compare(left.raw, right.raw)
}
