package jwt

import (
	
	
	
	
	
	
)

// TimePrecision sets the precision of times and dates within this library.
// This has an influence on the precision of times when comparing expiry or
// other related time fields. Furthermore, it is also the precision of times
// when serializing.
//
// For backwards compatibility the default precision is set to seconds, so that
// no fractional timestamps are generated.
var TimePrecision = time.Second

// MarshalSingleStringAsArray modifies the behaviour of the ClaimStrings type, especially
// its MarshalJSON function.
//
// If it is set to true (the default), it will always serialize the type as an
// array of strings, even if it just contains one element, defaulting to the behaviour
// of the underlying []string. If it is set to false, it will serialize to a single
// string, if it contains one element. Otherwise, it will serialize to an array of strings.
var MarshalSingleStringAsArray = true

// NumericDate represents a JSON numeric date value, as referenced at
// https://datatracker.ietf.org/doc/html/rfc7519#section-2.
type NumericDate struct {
	time.Time
}

// NewNumericDate constructs a new *NumericDate from a standard library time.Time struct.
// It will truncate the timestamp according to the precision specified in TimePrecision.
func ( time.Time) *NumericDate {
	return &NumericDate{.Truncate(TimePrecision)}
}

// newNumericDateFromSeconds creates a new *NumericDate out of a float64 representing a
// UNIX epoch with the float fraction representing non-integer seconds.
func newNumericDateFromSeconds( float64) *NumericDate {
	,  := math.Modf()
	return NewNumericDate(time.Unix(int64(), int64(*1e9)))
}

// MarshalJSON is an implementation of the json.RawMessage interface and serializes the UNIX epoch
// represented in NumericDate to a byte array, using the precision specified in TimePrecision.
func ( NumericDate) () ( []byte,  error) {
	var  int
	if TimePrecision < time.Second {
		 = int(math.Log10(float64(time.Second) / float64(TimePrecision)))
	}
	 := .Truncate(TimePrecision)

	// For very large timestamps, UnixNano would overflow an int64, but this
	// function requires nanosecond level precision, so we have to use the
	// following technique to get round the issue:
	// 1. Take the normal unix timestamp to form the whole number part of the
	//    output,
	// 2. Take the result of the Nanosecond function, which retuns the offset
	//    within the second of the particular unix time instance, to form the
	//    decimal part of the output
	// 3. Concatenate them to produce the final result
	 := strconv.FormatInt(.Unix(), 10)
	 := strconv.FormatFloat(float64(.Nanosecond())/float64(time.Second), 'f', , 64)

	 := append([]byte(), []byte()[1:]...)

	return , nil
}

// UnmarshalJSON is an implementation of the json.RawMessage interface and deserializses a
// NumericDate from a JSON representation, i.e. a json.Number. This number represents an UNIX epoch
// with either integer or non-integer seconds.
func ( *NumericDate) ( []byte) ( error) {
	var (
		 json.Number
		      float64
	)

	if  = json.Unmarshal(, &);  != nil {
		return fmt.Errorf("could not parse NumericData: %w", )
	}

	if ,  = .Float64();  != nil {
		return fmt.Errorf("could not convert json number value to float: %w", )
	}

	 := newNumericDateFromSeconds()
	* = *

	return nil
}

// ClaimStrings is basically just a slice of strings, but it can be either serialized from a string array or just a string.
// This type is necessary, since the "aud" claim can either be a single string or an array.
type ClaimStrings []string

func ( *ClaimStrings) ( []byte) ( error) {
	var  interface{}

	if  = json.Unmarshal(, &);  != nil {
		return 
	}

	var  []string

	switch v := .(type) {
	case string:
		 = append(, )
	case []string:
		 = ClaimStrings()
	case []interface{}:
		for ,  := range  {
			,  := .(string)
			if ! {
				return &json.UnsupportedTypeError{Type: reflect.TypeOf()}
			}
			 = append(, )
		}
	case nil:
		return nil
	default:
		return &json.UnsupportedTypeError{Type: reflect.TypeOf()}
	}

	* = 

	return
}

func ( ClaimStrings) () ( []byte,  error) {
	// This handles a special case in the JWT RFC. If the string array, e.g. used by the "aud" field,
	// only contains one element, it MAY be serialized as a single string. This may or may not be
	// desired based on the ecosystem of other JWT library used, so we make it configurable by the
	// variable MarshalSingleStringAsArray.
	if len() == 1 && !MarshalSingleStringAsArray {
		return json.Marshal([0])
	}

	return json.Marshal([]string())
}