package resourcename

import (
	"fmt"
	"unicode"
)

// Validate that a resource name conforms to the restrictions outlined in AIP-122.
// See: https://google.aip.dev/122
func Validate(name string) error {
	if name == "" {
		return fmt.Errorf("empty")
	}
	var sc Scanner
	sc.Init(name)
	var i int
	for sc.Scan() {
		i++
		segment := sc.Segment()
		switch {
		case segment == "":
			return fmt.Errorf("segment %d is empty", i)
		case segment == Wildcard:
			continue
		case segment.IsVariable():
			return fmt.Errorf("segment '%s': valid resource names must not contain variables", sc.Segment())
		case !isDomainName(string(sc.Segment())):
			return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment())
		}
	}
	if sc.Full() && !isDomainName(sc.ServiceName()) {
		return fmt.Errorf("service '%s': not a valid DNS name", sc.Segment())
	}
	return nil
}

// ValidatePattern that a resource name pattern conforms to the restrictions outlined in AIP-122.
// See: https://google.aip.dev/122
func ValidatePattern(pattern string) error {
	if pattern == "" {
		return fmt.Errorf("empty")
	}
	var sc Scanner
	sc.Init(pattern)
	var i int
	for sc.Scan() {
		i++
		segment := sc.Segment()
		switch {
		case segment == "":
			return fmt.Errorf("segment %d is empty", i)
		case segment == Wildcard:
			return fmt.Errorf("segment '%d': wildcards not allowed in patterns", i)
		case segment.IsVariable():
			switch {
			case segment.Literal() == "":
				return fmt.Errorf("segment '%s': missing variable name", sc.Segment())
			case !isSnakeCase(string(segment.Literal())):
				return fmt.Errorf("segment '%s': must be valid snake case", sc.Segment())
			}
		case !isDomainName(string(sc.Segment())):
			return fmt.Errorf("segment '%s': not a valid DNS name", sc.Segment())
		}
	}
	if sc.Full() {
		return fmt.Errorf("patterns can not be full resource names")
	}
	return nil
}

func isSnakeCase(s string) bool {
	for i, r := range s {
		if i == 0 {
			if !unicode.IsLower(r) {
				return false
			}
		} else if !(r == '_' || unicode.In(r, unicode.Lower, unicode.Digit)) {
			return false
		}
	}
	return true
}
