package layoutauthoringv1

import (
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

var (
	jsonFieldPattern = regexp.MustCompile(`^[a-z][a-z0-9_]*$`)
	jsonIndexPattern = regexp.MustCompile(`^(0|[1-9][0-9]*)$`)
)

// Compatibility disposition: root release v0.3.0 introduced FieldReference
// before any released wire-go version or Core producer could persist Layout
// command failures. The first producer must pin a corrected root release, so
// there is no supported durable legacy state to migrate and no opaque-string
// read branch to retain. Values from the pre-producer fixtures are rejected
// below instead of becoming a second public field vocabulary.

// validCommandFailureFieldReference keeps the semantic request shape as the
// single source of truth for caller-visible failure locations. A status read
// without its original SubmitRequest can prove that the pointer names a field
// in some CommandOperation shape. Submit-bound validation additionally proves
// that the pointer resolves in the exact accepted operation, including its
// selected arm, optional presence, and collection indexes.
func validCommandFailureFieldReference(reference string, operation *CommandOperation) bool {
	segments, ok := commandFailureFieldSegments(reference)
	if !ok || !semanticJSONPointerTypeExists(reflect.TypeOf(CommandOperation{}), segments) {
		return false
	}
	return operation == nil || semanticJSONPointerValueExists(reflect.ValueOf(*operation), segments)
}

// commandFailureFieldSegments parses the deliberately restricted canonical
// JSON Pointer grammar used by CommandFailure. CommandOperation JSON field
// names are lower snake case and collection positions are canonical decimal
// indexes, so escape sequences and arbitrary map-key segments are not part of
// this contract.
func commandFailureFieldSegments(reference string) ([]string, bool) {
	if len(reference) == 0 || len(reference) > MaxFailureFieldBytes || reference[0] != '/' ||
		strings.TrimSpace(reference) != reference {
		return nil, false
	}
	segments := strings.Split(reference[1:], "/")
	if len(segments) < 2 {
		return nil, false
	}
	for _, segment := range segments {
		if !jsonFieldPattern.MatchString(segment) && !jsonIndexPattern.MatchString(segment) {
			return nil, false
		}
	}
	return segments, true
}

func semanticJSONPointerTypeExists(valueType reflect.Type, segments []string) bool {
	for valueType.Kind() == reflect.Pointer {
		valueType = valueType.Elem()
	}
	if len(segments) == 0 {
		return true
	}
	switch valueType.Kind() {
	case reflect.Struct:
		field, _, ok := semanticJSONField(valueType, segments[0])
		return ok && semanticJSONPointerTypeExists(field.Type, segments[1:])
	case reflect.Slice, reflect.Array:
		if _, ok := parseJSONIndex(segments[0]); !ok {
			return false
		}
		return semanticJSONPointerTypeExists(valueType.Elem(), segments[1:])
	default:
		return false
	}
}

func semanticJSONPointerValueExists(value reflect.Value, segments []string) bool {
	for value.Kind() == reflect.Pointer || value.Kind() == reflect.Interface {
		if value.IsNil() {
			return false
		}
		value = value.Elem()
	}
	if len(segments) == 0 {
		return true
	}
	switch value.Kind() {
	case reflect.Struct:
		field, index, ok := semanticJSONField(value.Type(), segments[0])
		if !ok {
			return false
		}
		fieldValue := value.Field(index)
		if field.OmitEmpty && fieldValue.IsZero() {
			return false
		}
		return semanticJSONPointerValueExists(fieldValue, segments[1:])
	case reflect.Slice, reflect.Array:
		index, ok := parseJSONIndex(segments[0])
		return ok && index < value.Len() && semanticJSONPointerValueExists(value.Index(index), segments[1:])
	default:
		return false
	}
}

type semanticJSONFieldInfo struct {
	Type      reflect.Type
	OmitEmpty bool
}

func semanticJSONField(valueType reflect.Type, name string) (semanticJSONFieldInfo, int, bool) {
	for index := 0; index < valueType.NumField(); index++ {
		field := valueType.Field(index)
		if field.PkgPath != "" {
			continue
		}
		tag, exists := field.Tag.Lookup("json")
		if !exists {
			continue
		}
		parts := strings.Split(tag, ",")
		if parts[0] != name {
			continue
		}
		info := semanticJSONFieldInfo{Type: field.Type}
		for _, option := range parts[1:] {
			if option == "omitempty" {
				info.OmitEmpty = true
				break
			}
		}
		return info, index, true
	}
	return semanticJSONFieldInfo{}, 0, false
}

func parseJSONIndex(value string) (int, bool) {
	if !jsonIndexPattern.MatchString(value) {
		return 0, false
	}
	index, err := strconv.ParseUint(value, 10, 63)
	if err != nil || uint64(int(index)) != index {
		return 0, false
	}
	return int(index), true
}
