// Package ns: validate.go implements namespace name validation
// (REQ-120, F4 — path traversal hardening). A namespace name is used
// to construct a filesystem path via filepath.Join(Root(), name); a
// name containing "..", "/", or shell-relevant characters could
// traverse outside ORCA_HOME or inject into SSH commands. ValidateName
// rejects any name that is not safe for both path construction and
// shell interpolation.
package ns

import (
	"fmt"
	"strings"
	"unicode"
)

// ValidateName returns an error if the namespace name is not safe for
// filesystem path construction or shell interpolation. A safe name:
//   - is non-empty and at most 128 characters;
//   - contains only printable, non-space runes;
//   - does not contain "/", "\", "..", a leading "-", null bytes, or
//     any control character;
//   - is not a reserved name ("cluster", "_defaults").
//
// The reserved-name check here is defensive; the CLI also enforces it.
// ValidateName is the single choke-point for any code path that
// converts a user-supplied namespace name into a path or a shell token.
func ValidateName(name string) error {
	if name == "" {
		return fmt.Errorf("namespace name is empty")
	}
	if len(name) > 128 {
		return fmt.Errorf("namespace name %q exceeds 128 characters", name)
	}
	if name == "cluster" {
		return fmt.Errorf("name %q is reserved for the cluster-wide dir", name)
	}
	if strings.Contains(name, "..") {
		return fmt.Errorf("namespace name %q contains \"..\" (path traversal)", name)
	}
	if strings.ContainsAny(name, `/\`) {
		return fmt.Errorf("namespace name %q contains a path separator", name)
	}
	if strings.HasPrefix(name, "-") {
		return fmt.Errorf("namespace name %q starts with '-' (shell flag injection)", name)
	}
	for _, r := range name {
		if r == 0 {
			return fmt.Errorf("namespace name %q contains a null byte", name)
		}
		if unicode.IsControl(r) {
			return fmt.Errorf("namespace name %q contains a control character", name)
		}
		if unicode.IsSpace(r) {
			return fmt.Errorf("namespace name %q contains a space", name)
		}
	}
	return nil
}
