// Copyright 2025 The JSON Schema Go Project Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package jsonschema

import (
	
	
	
	
	
	
	
	
	
	
	
	
)

// Equal reports whether two Go values representing JSON values are equal according
// to the JSON Schema spec.
// The values must not contain cycles.
// See https://json-schema.org/draft/2020-12/json-schema-core#section-4.2.2.
// It behaves like reflect.DeepEqual, except that numbers are compared according
// to mathematical equality.
func (,  any) bool {
	return equalValue(reflect.ValueOf(), reflect.ValueOf())
}

func equalValue(,  reflect.Value) bool {
	// Copied from src/reflect/deepequal.go, omitting the visited check (because JSON
	// values are trees).
	if !.IsValid() || !.IsValid() {
		return .IsValid() == .IsValid()
	}

	// Treat numbers specially.
	,  := jsonNumber()
	,  := jsonNumber()
	if  &&  {
		return .Cmp() == 0
	}
	if .Kind() != .Kind() {
		return false
	}
	switch .Kind() {
	case reflect.Array:
		if .Len() != .Len() {
			return false
		}
		for  := range .Len() {
			if !(.Index(), .Index()) {
				return false
			}
		}
		return true
	case reflect.Slice:
		if .IsNil() != .IsNil() {
			return false
		}
		if .Len() != .Len() {
			return false
		}
		if .UnsafePointer() == .UnsafePointer() {
			return true
		}
		// Special case for []byte, which is common.
		if .Type().Elem().Kind() == reflect.Uint8 && .Type() == .Type() {
			return bytes.Equal(.Bytes(), .Bytes())
		}
		for  := range .Len() {
			if !(.Index(), .Index()) {
				return false
			}
		}
		return true
	case reflect.Interface:
		if .IsNil() || .IsNil() {
			return .IsNil() == .IsNil()
		}
		return (.Elem(), .Elem())
	case reflect.Pointer:
		if .UnsafePointer() == .UnsafePointer() {
			return true
		}
		return (.Elem(), .Elem())
	case reflect.Struct:
		 := .Type()
		if  != .Type() {
			return false
		}
		for  := range .NumField() {
			 := .Field()
			if !.IsExported() {
				continue
			}
			if !(.FieldByIndex(.Index), .FieldByIndex(.Index)) {
				return false
			}
		}
		return true
	case reflect.Map:
		if .IsNil() != .IsNil() {
			return false
		}
		if .Len() != .Len() {
			return false
		}
		if .UnsafePointer() == .UnsafePointer() {
			return true
		}
		 := .MapRange()
		for .Next() {
			 := .Value()
			 := .MapIndex(.Key())
			if !.IsValid() || !(, ) {
				return false
			}
		}
		return true
	case reflect.Func:
		if .Type() != .Type() {
			return false
		}
		if .IsNil() && .IsNil() {
			return true
		}
		panic("cannot compare functions")
	case reflect.String:
		return .String() == .String()
	case reflect.Bool:
		return .Bool() == .Bool()
	// Ints, uints and floats handled in jsonNumber, at top of function.
	default:
		panic(fmt.Sprintf("unsupported kind: %s", .Kind()))
	}
}

// hashValue adds v to the data hashed by h. v must not have cycles.
// hashValue panics if the value contains functions or channels, or maps whose
// key type is not string.
// It ignores unexported fields of structs.
// Calls to hashValue with the equal values (in the sense
// of [Equal]) result in the same sequence of values written to the hash.
func hashValue( *maphash.Hash,  reflect.Value) {
	// TODO: replace writes of basic types with WriteComparable in 1.24.

	 := func( uint64) {
		var  [8]byte
		binary.BigEndian.PutUint64([:], )
		.Write([:])
	}

	var  func(reflect.Value)
	 = func( reflect.Value) {
		if ,  := jsonNumber();  {
			// We want 1.0 and 1 to hash the same.
			// big.Rats are always normalized, so they will be.
			// We could do this more efficiently by handling the int and float cases
			// separately, but that's premature.
			(uint64(.Sign() + 1))
			.Write(.Num().Bytes())
			.Write(.Denom().Bytes())
			return
		}
		switch .Kind() {
		case reflect.Invalid:
			.WriteByte(0)
		case reflect.String:
			.WriteString(.String())
		case reflect.Bool:
			if .Bool() {
				.WriteByte(1)
			} else {
				.WriteByte(0)
			}
		case reflect.Complex64, reflect.Complex128:
			 := .Complex()
			(math.Float64bits(real()))
			(math.Float64bits(imag()))
		case reflect.Array, reflect.Slice:
			// Although we could treat []byte more efficiently,
			// JSON values are unlikely to contain them.
			(uint64(.Len()))
			for  := range .Len() {
				(.Index())
			}
		case reflect.Interface, reflect.Pointer:
			(.Elem())
		case reflect.Struct:
			 := .Type()
			for  := range .NumField() {
				if  := .Field(); .IsExported() {
					(.FieldByIndex(.Index))
				}
			}
		case reflect.Map:
			if .Type().Key().Kind() != reflect.String {
				panic("map with non-string key")
			}
			// Sort the keys so the hash is deterministic.
			 := .MapKeys()
			// Write the length. That distinguishes between, say, two consecutive
			// maps with disjoint keys from one map that has the items of both.
			(uint64(len()))
			slices.SortFunc(, func(,  reflect.Value) int { return cmp.Compare(.String(), .String()) })
			for ,  := range  {
				()
				(.MapIndex())
			}
		// Ints, uints and floats handled in jsonNumber, at top of function.
		default:
			panic(fmt.Sprintf("unsupported kind: %s", .Kind()))
		}
	}

	()
}

// jsonNumber converts a numeric value or a json.Number to a [big.Rat].
// If v is not a number, it returns nil, false.
func jsonNumber( reflect.Value) (*big.Rat, bool) {
	 := new(big.Rat)
	switch {
	case !.IsValid():
		return nil, false
	case .CanInt():
		.SetInt64(.Int())
	case .CanUint():
		.SetUint64(.Uint())
	case .CanFloat():
		.SetFloat64(.Float())
	default:
		,  := .Interface().(json.Number)
		if ! {
			return nil, false
		}
		if ,  := .SetString(.String()); ! {
			// This can fail in rare cases; for example, "1e9999999".
			// That is a valid JSON number, since the spec puts no limit on the size
			// of the exponent.
			return nil, false
		}
	}
	return , true
}

// jsonType returns a string describing the type of the JSON value,
// as described in the JSON Schema specification:
// https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.1.1.
// It returns "", false if the value is not valid JSON.
func jsonType( reflect.Value) (string, bool) {
	if !.IsValid() {
		// Not v.IsNil(): a nil []any is still a JSON array.
		return "null", true
	}
	if .CanInt() || .CanUint() {
		return "integer", true
	}
	if .CanFloat() {
		if ,  := math.Modf(.Float());  == 0 {
			return "integer", true
		}
		return "number", true
	}
	switch .Kind() {
	case reflect.Bool:
		return "boolean", true
	case reflect.String:
		return "string", true
	case reflect.Slice, reflect.Array:
		return "array", true
	case reflect.Map, reflect.Struct:
		return "object", true
	default:
		return "", false
	}
}

func assert( bool,  string) {
	if ! {
		panic("assertion failed: " + )
	}
}

// marshalStructWithMap marshals its first argument to JSON, treating the field named
// mapField as an embedded map. The first argument must be a pointer to
// a struct. The underlying type of mapField must be a map[string]any, and it must have
// a "-" json tag, meaning it will not be marshaled.
//
// For example, given this struct:
//
//	type S struct {
//	   A int
//	   Extra map[string] any `json:"-"`
//	}
//
// and this value:
//
//	s := S{A: 1, Extra: map[string]any{"B": 2}}
//
// the call marshalJSONWithMap(s, "Extra") would return
//
//	{"A": 1, "B": 2}
//
// It is an error if the map contains the same key as another struct field's
// JSON name.
//
// marshalStructWithMap calls json.Marshal on a value of type T, so T must not
// have a MarshalJSON method that calls this function, on pain of infinite regress.
//
// Note that there is a similar function in mcp/util.go, but they are not the same.
// Here the function requires `-` json tag, does not clear the mapField map,
// and handles embedded struct due to the implementation of jsonNames in this package.
//
// TODO: avoid this restriction on T by forcing it to marshal in a default way.
// See https://go.dev/play/p/EgXKJHxEx_R.
func marshalStructWithMap[ any]( *,  string) ([]byte, error) {
	// Marshal the struct and the map separately, and concatenate the bytes.
	// This strategy is dramatically less complicated than
	// constructing a synthetic struct or map with the combined keys.
	if  == nil {
		return []byte("null"), nil
	}
	 := *
	 := reflect.ValueOf(&).Elem().FieldByName()
	 := .Interface().(map[string]any)

	// Check for duplicates.
	 := jsonNames(reflect.TypeFor[]())
	for  := range  {
		if [] {
			return nil, fmt.Errorf("map key %q duplicates struct field", )
		}
	}

	,  := json.Marshal()
	if  != nil {
		return nil, fmt.Errorf("marshalStructWithMap(%+v): %w", , )
	}
	if len() == 0 {
		return , nil
	}
	,  := json.Marshal()
	if  != nil {
		return nil, 
	}
	if len() == 2 { // must be "{}"
		return , nil
	}
	// "{X}" + "{Y}" => "{X,Y}"
	 := append([:len()-1], ',')
	 = append(, [1:]...)
	return , nil
}

// unmarshalStructWithMap is the inverse of marshalStructWithMap.
// T has the same restrictions as in that function.
//
// Note that there is a similar function in mcp/util.go, but they are not the same.
// Here jsonNames also returns fields from embedded structs, hence this function
// handles embedded structs as well.
func unmarshalStructWithMap[ any]( []byte,  *,  string) error {
	// Unmarshal into the struct, ignoring unknown fields.
	if  := json.Unmarshal(, );  != nil {
		return 
	}
	// Unmarshal into the map.
	 := map[string]any{}
	if  := json.Unmarshal(, &);  != nil {
		return 
	}
	// Delete from the map the fields of the struct.
	for  := range jsonNames(reflect.TypeFor[]()) {
		delete(, )
	}
	if len() != 0 {
		reflect.ValueOf().Elem().FieldByName().Set(reflect.ValueOf())
	}
	return nil
}

var jsonNamesMap sync.Map // from reflect.Type to map[string]bool

// jsonNames returns the set of JSON object keys that t will marshal into,
// including fields from embedded structs in t.
// t must be a struct type.
//
// Note that there is a similar function in mcp/util.go, but they are not the same
// Here the function recurses over embedded structs and includes fields from them.
func jsonNames( reflect.Type) map[string]bool {
	// Lock not necessary: at worst we'll duplicate work.
	if ,  := jsonNamesMap.Load();  {
		return .(map[string]bool)
	}
	 := map[string]bool{}
	for  := range .NumField() {
		 := .Field()
		// handle embedded structs
		if .Anonymous {
			 := .Type
			if .Kind() == reflect.Ptr {
				 = .Elem()
			}
			for  := range () {
				[] = true
			}
			continue
		}
		 := fieldJSONInfo()
		if !.omit {
			[.name] = true
		}
	}
	jsonNamesMap.Store(, )
	return 
}

type jsonInfo struct {
	omit     bool            // unexported or first tag element is "-"
	name     string          // Go field name or first tag element. Empty if omit is true.
	settings map[string]bool // "omitempty", "omitzero", etc.
}

// fieldJSONInfo reports information about how encoding/json
// handles the given struct field.
// If the field is unexported, jsonInfo.omit is true and no other jsonInfo field
// is populated.
// If the field is exported and has no tag, then name is the field's name and all
// other fields are false.
// Otherwise, the information is obtained from the tag.
func fieldJSONInfo( reflect.StructField) jsonInfo {
	if !.IsExported() {
		return jsonInfo{omit: true}
	}
	 := jsonInfo{name: .Name}
	if ,  := .Tag.Lookup("json");  {
		, ,  := strings.Cut(, ",")
		// "-" means omit, but "-," means the name is "-"
		if  == "-" && ! {
			return jsonInfo{omit: true}
		}
		if  != "" {
			.name = 
		}
		if len() > 0 {
			.settings = map[string]bool{}
			for ,  := range strings.Split(, ",") {
				.settings[] = true
			}
		}
	}
	return 
}

// wrapf wraps *errp with the given formatted message if *errp is not nil.
func wrapf( *error,  string,  ...any) {
	if * != nil {
		* = fmt.Errorf("%s: %w", fmt.Sprintf(, ...), *)
	}
}