// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build !goexperiment.jsonv2 || !go1.25

package json

import (
	"encoding"
	"errors"
	"io"
	"reflect"

	"github.com/metacubex/jsonv2/internal"
	"github.com/metacubex/jsonv2/internal/jsonflags"
	"github.com/metacubex/jsonv2/internal/jsonopts"
	"github.com/metacubex/jsonv2/internal/jsonwire"
	"github.com/metacubex/jsonv2/jsontext"
)

var errNonStringValue = errors.New("JSON value must be string type")

// Interfaces for custom serialization.
var (
	jsonMarshalerType       = typeFor[Marshaler]()
	jsonMarshalerToType     = typeFor[MarshalerTo]()
	jsonUnmarshalerType     = typeFor[Unmarshaler]()
	jsonUnmarshalerFromType = typeFor[UnmarshalerFrom]()
	textAppenderType        = typeFor[TextAppender]()
	textMarshalerType       = typeFor[encoding.TextMarshaler]()
	textUnmarshalerType     = typeFor[encoding.TextUnmarshaler]()

	allMarshalerTypes   = []reflect.Type{jsonMarshalerToType, jsonMarshalerType, textAppenderType, textMarshalerType}
	allUnmarshalerTypes = []reflect.Type{jsonUnmarshalerFromType, jsonUnmarshalerType, textUnmarshalerType}
	allMethodTypes      = append(allMarshalerTypes, allUnmarshalerTypes...)
)

// Marshaler is implemented by types that can marshal themselves.
// It is recommended that types implement [MarshalerTo] unless the implementation
// is trying to avoid a hard dependency on the "jsontext" package.
//
// It is recommended that implementations return a buffer that is safe
// for the caller to retain and potentially mutate.
//
// If the returned error is a [SemanticError], then unpopulated fields
// of the error may be populated by [json] with additional context.
// Errors of other types are wrapped within a [SemanticError].
type Marshaler interface {
	MarshalJSON() ([]byte, error)
}

// MarshalerTo is implemented by types that can marshal themselves.
// It is recommended that types implement MarshalerTo instead of [Marshaler]
// since this is both more performant and flexible.
// If a type implements both Marshaler and MarshalerTo,
// then MarshalerTo takes precedence. In such a case, both implementations
// should aim to have equivalent behavior for the default marshal options.
//
// The implementation must write only one JSON value to the Encoder.
// Alternatively, it may return [ErrUnsupported] without mutating
// the Encoder. The "json" package calling the method will
// use the next available JSON representation for the receiver type.
// Implementations must not retain the pointer to [jsontext.Encoder].
//
// If the returned error is a [SemanticError], then unpopulated fields
// of the error may be populated by [json] with additional context.
// Errors of other types are wrapped within a [SemanticError],
// unless it is an IO error.
//
// The MarshalJSONTo method should not be directly called as it may
// return sentinel errors that need special handling.
// Users should instead call [MarshalEncode], which handles such cases.
type MarshalerTo interface {
	MarshalJSONTo(*jsontext.Encoder) error
}

// Unmarshaler is implemented by types that can unmarshal themselves.
// It is recommended that types implement [UnmarshalerFrom] unless the implementation
// is trying to avoid a hard dependency on the "jsontext" package.
//
// The input can be assumed to be a valid encoding of a JSON value
// if called from unmarshal functionality in this package.
// UnmarshalJSON must copy the JSON data if it is retained after returning.
// It is recommended that UnmarshalJSON implement merge semantics when
// unmarshaling into a pre-populated value.
//
// Implementations must not retain or mutate the input []byte.
//
// If the returned error is a [SemanticError], then unpopulated fields
// of the error may be populated by [json] with additional context.
// Errors of other types are wrapped within a [SemanticError].
type Unmarshaler interface {
	UnmarshalJSON([]byte) error
}

// UnmarshalerFrom is implemented by types that can unmarshal themselves.
// It is recommended that types implement UnmarshalerFrom instead of [Unmarshaler]
// since this is both more performant and flexible.
// If a type implements both Unmarshaler and UnmarshalerFrom,
// then UnmarshalerFrom takes precedence. In such a case, both implementations
// should aim to have equivalent behavior for the default unmarshal options.
//
// The implementation must read only one JSON value from the Decoder.
// It is recommended that UnmarshalJSONFrom implement merge semantics when
// unmarshaling into a pre-populated value.
// Alternatively, it may return [ErrUnsupported] without mutating
// the Decoder. The "json" package calling the method will
// use the next available JSON representation for the receiver type.
// Implementations must not retain the pointer to [jsontext.Decoder].
//
// If the returned error is a [SemanticError], then unpopulated fields
// of the error may be populated by [json] with additional context.
// Errors of other types are wrapped within a [SemanticError],
// unless it is a [jsontext.SyntacticError] or an IO error.
//
// The UnmarshalJSONFrom method should not be directly called as it may
// return sentinel errors that need special handling.
// Users should instead call [UnmarshalDecode], which handles such cases.
type UnmarshalerFrom interface {
	UnmarshalJSONFrom(*jsontext.Decoder) error
}

func makeMethodArshaler(fncs *arshaler, t reflect.Type) *arshaler {
	// Avoid injecting method arshaler on the pointer or interface version
	// to avoid ever calling the method on a nil pointer or interface receiver.
	// Let it be injected on the value receiver (which is always addressable).
	if t.Kind() == reflect.Pointer || t.Kind() == reflect.Interface {
		return fncs
	}

	if needAddr, ok := implements(t, textMarshalerType); ok {
		fncs.nonDefault = true
		prevMarshal := fncs.marshal
		fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
			if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				(needAddr && va.forcedAddr) {
				return prevMarshal(enc, va, mo)
			}
			marshaler := va.Addr().Interface().(encoding.TextMarshaler)
			if err := export.Encoder(enc).AppendRaw('"', false, func(b []byte) ([]byte, error) {
				b2, err := marshaler.MarshalText()
				return append(b, b2...), err
			}); err != nil {
				err = wrapErrUnsupported(err, "MarshalText method")
				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalText") // unlike unmarshal, always wrapped
				}
				if !isSemanticError(err) && !export.IsIOError(err) {
					err = newMarshalErrorBefore(enc, t, err)
				}
				return err
			}
			return nil
		}
	}

	if needAddr, ok := implements(t, textAppenderType); ok {
		fncs.nonDefault = true
		prevMarshal := fncs.marshal
		fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) (err error) {
			if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				(needAddr && va.forcedAddr) {
				return prevMarshal(enc, va, mo)
			}
			appender := va.Addr().Interface().(TextAppender)
			if err := export.Encoder(enc).AppendRaw('"', false, appender.AppendText); err != nil {
				err = wrapErrUnsupported(err, "AppendText method")
				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(va.Addr().Interface(), err, "AppendText") // unlike unmarshal, always wrapped
				}
				if !isSemanticError(err) && !export.IsIOError(err) {
					err = newMarshalErrorBefore(enc, t, err)
				}
				return err
			}
			return nil
		}
	}

	if needAddr, ok := implements(t, jsonMarshalerType); ok {
		fncs.nonDefault = true
		prevMarshal := fncs.marshal
		fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
			if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
				return prevMarshal(enc, va, mo)
			}
			marshaler := va.Addr().Interface().(Marshaler)
			val, err := marshaler.MarshalJSON()
			if err != nil {
				err = wrapErrUnsupported(err, "MarshalJSON method")
				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
				}
				err = newMarshalErrorBefore(enc, t, err)
				return collapseSemanticErrors(err)
			}
			if err := enc.WriteValue(val); err != nil {
				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSON") // unlike unmarshal, always wrapped
				}
				if isSyntacticError(err) {
					err = newMarshalErrorBefore(enc, t, err)
				}
				return err
			}
			return nil
		}
	}

	if needAddr, ok := implements(t, jsonMarshalerToType); ok {
		fncs.nonDefault = true
		prevMarshal := fncs.marshal
		fncs.marshal = func(enc *jsontext.Encoder, va addressableValue, mo *jsonopts.Struct) error {
			if mo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				((needAddr && va.forcedAddr) || export.Encoder(enc).Tokens.Last.NeedObjectName()) {
				return prevMarshal(enc, va, mo)
			}
			xe := export.Encoder(enc)
			prevDepth, prevLength := xe.Tokens.DepthLength()
			xe.Flags.Set(jsonflags.WithinArshalCall | 1)
			marshaler := va.Addr().Interface().(MarshalerTo)
			err := marshaler.MarshalJSONTo(enc)
			xe.Flags.Set(jsonflags.WithinArshalCall | 0)
			currDepth, currLength := xe.Tokens.DepthLength()
			if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
				err = errNonSingularValue
			}
			if err != nil {
				if errors.Is(err, ErrUnsupported) {
					if prevDepth == currDepth && prevLength == currLength {
						return prevMarshal(enc, va, mo)
					}
					err = errUnsupportedMutation
				}
				if mo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return internal.NewMarshalerError(va.Addr().Interface(), err, "MarshalJSONTo") // unlike unmarshal, always wrapped
				}
				if !export.IsIOError(err) {
					err = newSemanticErrorWithPosition(enc, t, prevDepth, prevLength, err)
				}
				return err
			}
			return nil
		}
	}

	if _, ok := implements(t, textUnmarshalerType); ok {
		fncs.nonDefault = true
		fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
			xd := export.Decoder(dec)
			var flags jsonwire.ValueFlags
			val, err := xd.ReadValue(&flags)
			if err != nil {
				return err // must be a syntactic or I/O error
			}
			if val.Kind() == 'n' {
				if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
					va.SetZero()
				}
				return nil
			}
			if val.Kind() != '"' {
				return newUnmarshalErrorAfter(dec, t, errNonStringValue)
			}
			s := jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
			unmarshaler := va.Addr().Interface().(encoding.TextUnmarshaler)
			if err := unmarshaler.UnmarshalText(s); err != nil {
				err = wrapErrUnsupported(err, "UnmarshalText method")
				if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return err // unlike marshal, never wrapped
				}
				if !isSemanticError(err) && !isSyntacticError(err) && !export.IsIOError(err) {
					err = newUnmarshalErrorAfter(dec, t, err)
				}
				return err
			}
			return nil
		}
	}

	if _, ok := implements(t, jsonUnmarshalerType); ok {
		fncs.nonDefault = true
		prevUnmarshal := fncs.unmarshal
		fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
			if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				export.Decoder(dec).Tokens.Last.NeedObjectName() {
				return prevUnmarshal(dec, va, uo)
			}
			val, err := dec.ReadValue()
			if err != nil {
				return err // must be a syntactic or I/O error
			}
			unmarshaler := va.Addr().Interface().(Unmarshaler)
			if err := unmarshaler.UnmarshalJSON(val); err != nil {
				err = wrapErrUnsupported(err, "UnmarshalJSON method")
				if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					return err // unlike marshal, never wrapped
				}
				err = newUnmarshalErrorAfter(dec, t, err)
				return collapseSemanticErrors(err)
			}
			return nil
		}
	}

	if _, ok := implements(t, jsonUnmarshalerFromType); ok {
		fncs.nonDefault = true
		prevUnmarshal := fncs.unmarshal
		fncs.unmarshal = func(dec *jsontext.Decoder, va addressableValue, uo *jsonopts.Struct) error {
			if uo.Flags.Get(jsonflags.CallMethodsWithLegacySemantics) &&
				export.Decoder(dec).Tokens.Last.NeedObjectName() {
				return prevUnmarshal(dec, va, uo)
			}
			xd := export.Decoder(dec)
			prevDepth, prevLength := xd.Tokens.DepthLength()
			if prevDepth == 1 && xd.AtEOF() {
				return io.EOF // check EOF early to avoid fn reporting an EOF
			}
			xd.Flags.Set(jsonflags.WithinArshalCall | 1)
			unmarshaler := va.Addr().Interface().(UnmarshalerFrom)
			err := unmarshaler.UnmarshalJSONFrom(dec)
			xd.Flags.Set(jsonflags.WithinArshalCall | 0)
			currDepth, currLength := xd.Tokens.DepthLength()
			if (prevDepth != currDepth || prevLength+1 != currLength) && err == nil {
				err = errNonSingularValue
			}
			if err != nil {
				if errors.Is(err, ErrUnsupported) {
					if prevDepth == currDepth && prevLength == currLength {
						return prevUnmarshal(dec, va, uo)
					}
					err = errUnsupportedMutation
				}
				if uo.Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
					if err2 := xd.SkipUntil(prevDepth, prevLength+1); err2 != nil {
						return err2
					}
					return err // unlike marshal, never wrapped
				}
				if !isSyntacticError(err) && !export.IsIOError(err) {
					err = newSemanticErrorWithPosition(dec, t, prevDepth, prevLength, err)
				}
				return err
			}
			return nil
		}
	}

	return fncs
}

// implementsAny is like t.Implements(ifaceType) for a list of interfaces,
// but checks whether either t or reflect.PointerTo(t) implements the interface.
func implementsAny(t reflect.Type, ifaceTypes ...reflect.Type) bool {
	for _, ifaceType := range ifaceTypes {
		if _, ok := implements(t, ifaceType); ok {
			return true
		}
	}
	return false
}

// implements is like t.Implements(ifaceType) but checks whether
// either t or reflect.PointerTo(t) implements the interface.
// It also reports whether the value needs to be addressed
// in order to satisfy the interface.
func implements(t, ifaceType reflect.Type) (needAddr, ok bool) {
	switch {
	case t.Implements(ifaceType):
		return false, true
	case reflect.PointerTo(t).Implements(ifaceType):
		return true, true
	default:
		return false, false
	}
}
