package pack

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

func CheckHostCompatibility(manifest Manifest, hostVersion string) error {
	targetHost := strings.TrimSpace(manifest.TargetHost)
	if targetHost == "" {
		return fmt.Errorf("pack manifest target_host is required")
	}
	if targetHost != "sub2api" {
		return fmt.Errorf("pack target_host %q is not supported", targetHost)
	}

	normalizedHost, err := parseVersion(strings.TrimSpace(hostVersion))
	if err != nil {
		return fmt.Errorf("parse host version %q: %w", hostVersion, err)
	}
	minVersion := strings.TrimSpace(manifest.MinHostVersion)
	if minVersion != "" {
		cmp, err := compareVersions(normalizedHost.raw, minVersion)
		if err != nil {
			return fmt.Errorf("compare min_host_version: %w", err)
		}
		if cmp < 0 {
			return fmt.Errorf("host version %q is below min_host_version %q", hostVersion, minVersion)
		}
	}

	maxVersion := strings.TrimSpace(manifest.MaxHostVersion)
	if maxVersion != "" {
		ok, err := matchesMaxConstraint(normalizedHost.raw, maxVersion)
		if err != nil {
			return fmt.Errorf("compare max_host_version: %w", err)
		}
		if !ok {
			return fmt.Errorf("host version %q is above max_host_version %q", hostVersion, maxVersion)
		}
	}
	return nil
}

type parsedVersion struct {
	raw   string
	parts [3]int
}

func compareVersions(a, b string) (int, error) {
	left, err := parseVersion(a)
	if err != nil {
		return 0, err
	}
	right, err := parseVersion(b)
	if err != nil {
		return 0, err
	}
	for i := 0; i < len(left.parts); i++ {
		if left.parts[i] < right.parts[i] {
			return -1, nil
		}
		if left.parts[i] > right.parts[i] {
			return 1, nil
		}
	}
	return 0, nil
}

func matchesMaxConstraint(hostVersion, maxVersion string) (bool, error) {
	normalizedMax := normalizeVersion(maxVersion)
	if strings.HasSuffix(normalizedMax, ".x") {
		prefix := strings.TrimSuffix(normalizedMax, ".x")
		parts := strings.Split(prefix, ".")
		if len(parts) != 2 {
			return false, fmt.Errorf("wildcard max version %q must be in N.N.x format", maxVersion)
		}
		host, err := parseVersion(hostVersion)
		if err != nil {
			return false, err
		}
		major, err := strconv.Atoi(parts[0])
		if err != nil {
			return false, fmt.Errorf("parse major version %q: %w", parts[0], err)
		}
		minor, err := strconv.Atoi(parts[1])
		if err != nil {
			return false, fmt.Errorf("parse minor version %q: %w", parts[1], err)
		}
		if host.parts[0] < major {
			return true, nil
		}
		if host.parts[0] > major {
			return false, nil
		}
		return host.parts[1] <= minor, nil
	}

	cmp, err := compareVersions(hostVersion, maxVersion)
	if err != nil {
		return false, err
	}
	return cmp <= 0, nil
}

func parseVersion(value string) (parsedVersion, error) {
	normalized := normalizeVersion(value)
	if normalized == "" {
		return parsedVersion{}, fmt.Errorf("version is required")
	}
	parts := strings.Split(normalized, ".")
	if len(parts) != 3 {
		return parsedVersion{}, fmt.Errorf("version %q must be in N.N.N format", value)
	}

	var parsed parsedVersion
	parsed.raw = normalized
	for i, part := range parts {
		number, err := strconv.Atoi(part)
		if err != nil {
			return parsedVersion{}, fmt.Errorf("parse version segment %q: %w", part, err)
		}
		parsed.parts[i] = number
	}
	return parsed, nil
}

func normalizeVersion(value string) string {
	trimmed := strings.TrimSpace(value)
	trimmed = strings.TrimPrefix(trimmed, "v")
	trimmed = strings.TrimPrefix(trimmed, "V")
	return trimmed
}
