package nodegroup

import (
	"fmt"
	"regexp"
	"strconv"
)

var (
	specPattern         = regexp.MustCompile(`^\d+[cC]\d+[gG]$`)
	specParsePattern    = regexp.MustCompile(`^(\d+)[cC](\d+)[gG]$`)
	instanceTypePattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9]*([._-][a-zA-Z0-9]+)+$`)
)

func ValidateInstanceTypeSpec(spec *InstanceTypeSpec) error {
	if err := ValidateSpec(spec.Spec); err != nil {
		return fmt.Errorf("invalid spec: %w", err)
	}

	if err := ValidateCloudInstanceType(spec.InstanceType); err != nil {
		return fmt.Errorf("invalid instance_type: %w", err)
	}

	if spec.AvailableCPU != "" {
		if err := ValidateAvailableCPU(spec.AvailableCPU); err != nil {
			return fmt.Errorf("invalid available_cpu: %w", err)
		}
	}

	if spec.AvailableMemoryMi != "" {
		if err := ValidateAvailableMemoryMi(spec.AvailableMemoryMi); err != nil {
			return fmt.Errorf("invalid available_memory_mi: %w", err)
		}
	}

	return nil
}

func ValidateSpec(spec string) error {
	if spec == "" {
		return fmt.Errorf("spec is required")
	}

	if !specPattern.MatchString(spec) {
		return fmt.Errorf("spec must match pattern NcMg (e.g., 8C16G), got: %s", spec)
	}

	return nil
}

func ParseSpec(spec string) (uint32, uint32, error) {
	if err := ValidateSpec(spec); err != nil {
		return 0, 0, err
	}

	matches := specParsePattern.FindStringSubmatch(spec)
	if len(matches) != 3 {
		return 0, 0, fmt.Errorf("spec must match pattern NcMg (e.g., 8c16g)")
	}

	cpu, err := strconv.Atoi(matches[1])
	if err != nil {
		return 0, 0, fmt.Errorf("invalid cpu: %w", err)
	}
	mem, err := strconv.Atoi(matches[2])
	if err != nil {
		return 0, 0, fmt.Errorf("invalid memory: %w", err)
	}

	return uint32(cpu), uint32(mem), nil
}

func ValidateCloudInstanceType(instanceType string) error {
	if instanceType == "" {
		return fmt.Errorf("instance_type is required")
	}

	if !instanceTypePattern.MatchString(instanceType) {
		return fmt.Errorf("instance_type must use alphanumerics separated by '.', '-', or '_' (e.g., m5.2xlarge, n2-standard-8), got: %s", instanceType)
	}

	return nil
}

func ValidateAvailableCPU(cpu string) error {
	val, err := strconv.Atoi(cpu)
	if err != nil {
		return fmt.Errorf("available_cpu must be a number: %s", cpu)
	}

	if val <= 0 || val > 256 {
		return fmt.Errorf("available_cpu out of range (1-256): %d", val)
	}

	return nil
}

func ValidateAvailableMemoryMi(memory string) error {
	val, err := strconv.Atoi(memory)
	if err != nil {
		return fmt.Errorf("available_memory_mi must be a number: %s", memory)
	}

	// Max ~2 TiB in MiB (2 * 1024 * 1024 = 2097152 MiB)
	if val <= 0 || val > 2097152 {
		return fmt.Errorf("available_memory_mi out of range (1-2097152 MiB): %d", val)
	}

	return nil
}

func ValidateSharedPoolID(id string) error {
	if id == "" {
		return fmt.Errorf("shared_pool_id is required")
	}

	if len(id) < 3 || len(id) > 128 {
		return fmt.Errorf("shared_pool_id length must be 3-128 characters")
	}

	return nil
}
