// Copyright (C) 2022-2023  Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later

package lowmemjson

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strings"

	"git.lukeshu.com/go/lowmemjson/internal"
)

// ErrInvalidUnreadRune is returned to Decodable.DecodeJSON(scanner)
// implementations from scanner.UnreadRune() if the last operation was
// not a successful .ReadRune() call.
var ErrInvalidUnreadRune = errors.New("lowmemjson: invalid use of UnreadRune")

// parser errors ///////////////////////////////////////////////////////////////////////////////////

// ErrParserExceededMaxDepth is the base error that a
// *DecodeSyntaxError wraps when the depth of the JSON document
// exceeds 10000.
var ErrParserExceededMaxDepth = internal.ErrParserExceededMaxDepth

// low-level decode errors /////////////////////////////////////////////////////////////////////////
// These will be wrapped in a *DecodeError.

// A DecodeReadError is returned from Decode (wrapped in a
// *DecodeError) if there is an I/O error reading the input.
type DecodeReadError struct {
	Err    error
	Offset int64
}

func (e *DecodeReadError) Error() string {
	return fmt.Sprintf("json: I/O error at input byte %v: %v", e.Offset, e.Err)
}
func (e *DecodeReadError) Unwrap() error { return e.Err }

// A DecodeSyntaxError is returned from Decode (wrapped in a
// *DecodeError) if there is a syntax error in the input.
type DecodeSyntaxError struct {
	Err    error
	Offset int64
}

func (e *DecodeSyntaxError) Error() string {
	return fmt.Sprintf("json: syntax error at input byte %v: %v", e.Offset, e.Err)
}
func (e *DecodeSyntaxError) Unwrap() error { return e.Err }

// A DecodeTypeError is returned from Decode (wrapped in a
// *DecodeError) if the JSON input is not appropriate for the given Go
// type.
//
// If a .DecodeJSON, .UnmarshalJSON, or .UnmashaleText method returns
// an error, it is wrapped in a *DecodeTypeError.
type DecodeTypeError struct {
	JSONType string //  (optional)
	GoType   reflect.Type
	Offset   int64
	Err      error // (optional)
}

func (e *DecodeTypeError) Error() string {
	var buf strings.Builder
	buf.WriteString("json: cannot decode ")
	if e.JSONType != "" {
		fmt.Fprintf(&buf, "JSON %s ", e.JSONType)
	}
	fmt.Fprintf(&buf, "at input byte %v into Go %v", e.Offset, e.GoType)
	if e.Err != nil {
		fmt.Fprintf(&buf, ": %v", strings.TrimPrefix(e.Err.Error(), "json: "))
	}
	return buf.String()
}

func (e *DecodeTypeError) Unwrap() error { return e.Err }

// ErrDecodeNonEmptyInterface is the base error that a
// *DecodeTypeError wraps when Decode is asked to unmarshal into an
// `interface` type that has one or more methods.
var ErrDecodeNonEmptyInterface = errors.New("cannot decode into non-empty interface")

// high-level decode errors ////////////////////////////////////////////////////////////////////////

// A DecodeArgumentError is returned from Decode if the argument is
// not a non-nil pointer or is not settable.
//
// Alternatively, a *DecodeArgument error may be found inside of a
// *DecodeTypeError if the type being decoded into is not a type that
// can be decoded into (such as map with non-stringable type as keys).
//
//	type DecodeArgumentError struct {
//	    Type reflect.Type
//	}
type DecodeArgumentError = json.InvalidUnmarshalError

// A DecodeError is returned from Decode for all errors except for
// *DecodeArgumentError.
//
// A *DecodeError wraps *DecodeSyntaxError for malformed or illegal
// input, *DecodeTypeError for Go type issues, or *DecodeReadError for
// I/O errors.
type DecodeError struct {
	Field string // Where in the JSON the error was, in the form "v[idx][idx][idx]".
	Err   error  // What the error was.

	FieldParent string // for compat; the same as encoding/json.UnmarshalTypeError.Struct
	FieldName   string // for compat; the same as encoding/json.UnmarshalTypeError.Field
}

func (e *DecodeError) Error() string {
	return fmt.Sprintf("json: %s: %s", e.Field, strings.TrimPrefix(e.Err.Error(), "json: "))
}
func (e *DecodeError) Unwrap() error { return e.Err }

// encode errors ///////////////////////////////////////////////////////////////////////////////////

// An EncodeTypeError is returned by Encode when attempting to encode
// an unsupported type.
//
//	type EncodeTypeError struct {
//	    Type reflect.Type
//	}
type EncodeTypeError = json.UnsupportedTypeError

// An EncodeValueError is returned by Encode when attempting to encode
// an unsupported value (such as a datastructure with a cycle).
//
//	type UnsupportedValueError struct {
//	    Value reflect.Value
//	    Str   string
//	}
type EncodeValueError = json.UnsupportedValueError

// An EncodeMethodError either wraps an error that is returned from an
// object's method when encoding that object to JSON, or wraps a
// *ReEncodeSyntaxError for the method's output.
type EncodeMethodError struct {
	Type       reflect.Type // The Go type that the method is on
	SourceFunc string       // The method: "EncodeJSON", "MarshalJSON", or "MarshalText"
	Err        error        // The error that the method returned
}

func (e *EncodeMethodError) Error() string {
	return fmt.Sprintf("json: error calling %v for type %v: %v",
		e.SourceFunc, e.Type, strings.TrimPrefix(e.Err.Error(), "json: "))
}
func (e *EncodeMethodError) Unwrap() error { return e.Err }

// reencode errors /////////////////////////////////////////////////////////////////////////////////

// A ReEncodeSyntaxError is returned from ReEncoder's methods if there
// is a syntax error in the input.
type ReEncodeSyntaxError struct {
	Err    error
	Offset int64
}

func (e *ReEncodeSyntaxError) Error() string {
	return fmt.Sprintf("json: syntax error at input byte %v: %v", e.Offset, e.Err)
}
func (e *ReEncodeSyntaxError) Unwrap() error { return e.Err }