// This file is auto-generated by jwt/internal/cmd/gentoken/main.go. DO NOT EDIT

package jwt

import (
	"bytes"
	"context"
	"sort"
	"sync"
	"time"

	"github.com/lestrrat-go/iter/mapiter"
	"github.com/lestrrat-go/jwx/internal/base64"
	"github.com/lestrrat-go/jwx/internal/iter"
	"github.com/lestrrat-go/jwx/internal/json"
	"github.com/lestrrat-go/jwx/internal/pool"
	"github.com/lestrrat-go/jwx/jwt/internal/types"
	"github.com/pkg/errors"
)

const (
	AudienceKey   = "aud"
	ExpirationKey = "exp"
	IssuedAtKey   = "iat"
	IssuerKey     = "iss"
	JwtIDKey      = "jti"
	NotBeforeKey  = "nbf"
	SubjectKey    = "sub"
)

// Token represents a generic JWT token.
// which are type-aware (to an extent). Other claims may be accessed via the `Get`/`Set`
// methods but their types are not taken into consideration at all. If you have non-standard
// claims that you must frequently access, consider creating accessors functions
// like the following
//
// func SetFoo(tok jwt.Token) error
// func GetFoo(tok jwt.Token) (*Customtyp, error)
//
// Embedding jwt.Token into another struct is not recommended, becase
// jwt.Token needs to handle private claims, and this really does not
// work well when it is embedded in other structure
type Token interface {
	Audience() []string
	Expiration() time.Time
	IssuedAt() time.Time
	Issuer() string
	JwtID() string
	NotBefore() time.Time
	Subject() string
	PrivateClaims() map[string]interface{}
	Get(string) (interface{}, bool)
	Set(string, interface{}) error
	Remove(string) error
	Clone() (Token, error)
	Iterate(context.Context) Iterator
	Walk(context.Context, Visitor) error
	AsMap(context.Context) (map[string]interface{}, error)
}
type stdToken struct {
	mu            *sync.RWMutex
	audience      types.StringList   // https://tools.ietf.org/html/rfc7519#section-4.1.3
	expiration    *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.4
	issuedAt      *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.6
	issuer        *string            // https://tools.ietf.org/html/rfc7519#section-4.1.1
	jwtID         *string            // https://tools.ietf.org/html/rfc7519#section-4.1.7
	notBefore     *types.NumericDate // https://tools.ietf.org/html/rfc7519#section-4.1.5
	subject       *string            // https://tools.ietf.org/html/rfc7519#section-4.1.2
	privateClaims map[string]interface{}
}

type stdTokenMarshalProxy struct {
	Xaudience   types.StringList   `json:"aud,omitempty"`
	Xexpiration *types.NumericDate `json:"exp,omitempty"`
	XissuedAt   *types.NumericDate `json:"iat,omitempty"`
	Xissuer     *string            `json:"iss,omitempty"`
	XjwtID      *string            `json:"jti,omitempty"`
	XnotBefore  *types.NumericDate `json:"nbf,omitempty"`
	Xsubject    *string            `json:"sub,omitempty"`
}

// New creates a standard token, with minimal knowledge of
// possible claims. Standard claims include"aud", "exp", "iat", "iss", "jti", "nbf" and "sub".
// Convenience accessors are provided for these standard claims
func New() Token {
	return &stdToken{
		mu:            &sync.RWMutex{},
		privateClaims: make(map[string]interface{}),
	}
}

func (t *stdToken) Get(name string) (interface{}, bool) {
	t.mu.RLock()
	defer t.mu.RUnlock()
	switch name {
	case AudienceKey:
		if t.audience == nil {
			return nil, false
		}
		v := t.audience.Get()
		return v, true
	case ExpirationKey:
		if t.expiration == nil {
			return nil, false
		}
		v := t.expiration.Get()
		return v, true
	case IssuedAtKey:
		if t.issuedAt == nil {
			return nil, false
		}
		v := t.issuedAt.Get()
		return v, true
	case IssuerKey:
		if t.issuer == nil {
			return nil, false
		}
		v := *(t.issuer)
		return v, true
	case JwtIDKey:
		if t.jwtID == nil {
			return nil, false
		}
		v := *(t.jwtID)
		return v, true
	case NotBeforeKey:
		if t.notBefore == nil {
			return nil, false
		}
		v := t.notBefore.Get()
		return v, true
	case SubjectKey:
		if t.subject == nil {
			return nil, false
		}
		v := *(t.subject)
		return v, true
	default:
		v, ok := t.privateClaims[name]
		return v, ok
	}
}

func (t *stdToken) Remove(key string) error {
	t.mu.Lock()
	defer t.mu.Unlock()
	switch key {
	case AudienceKey:
		t.audience = nil
	case ExpirationKey:
		t.expiration = nil
	case IssuedAtKey:
		t.issuedAt = nil
	case IssuerKey:
		t.issuer = nil
	case JwtIDKey:
		t.jwtID = nil
	case NotBeforeKey:
		t.notBefore = nil
	case SubjectKey:
		t.subject = nil
	default:
		delete(t.privateClaims, key)
	}
	return nil
}

func (t *stdToken) Set(name string, value interface{}) error {
	t.mu.Lock()
	defer t.mu.Unlock()
	return t.setNoLock(name, value)
}

func (t *stdToken) setNoLock(name string, value interface{}) error {
	switch name {
	case AudienceKey:
		var acceptor types.StringList
		if err := acceptor.Accept(value); err != nil {
			return errors.Wrapf(err, `invalid value for %s key`, AudienceKey)
		}
		t.audience = acceptor
		return nil
	case ExpirationKey:
		var acceptor types.NumericDate
		if err := acceptor.Accept(value); err != nil {
			return errors.Wrapf(err, `invalid value for %s key`, ExpirationKey)
		}
		t.expiration = &acceptor
		return nil
	case IssuedAtKey:
		var acceptor types.NumericDate
		if err := acceptor.Accept(value); err != nil {
			return errors.Wrapf(err, `invalid value for %s key`, IssuedAtKey)
		}
		t.issuedAt = &acceptor
		return nil
	case IssuerKey:
		if v, ok := value.(string); ok {
			t.issuer = &v
			return nil
		}
		return errors.Errorf(`invalid value for %s key: %T`, IssuerKey, value)
	case JwtIDKey:
		if v, ok := value.(string); ok {
			t.jwtID = &v
			return nil
		}
		return errors.Errorf(`invalid value for %s key: %T`, JwtIDKey, value)
	case NotBeforeKey:
		var acceptor types.NumericDate
		if err := acceptor.Accept(value); err != nil {
			return errors.Wrapf(err, `invalid value for %s key`, NotBeforeKey)
		}
		t.notBefore = &acceptor
		return nil
	case SubjectKey:
		if v, ok := value.(string); ok {
			t.subject = &v
			return nil
		}
		return errors.Errorf(`invalid value for %s key: %T`, SubjectKey, value)
	default:
		if t.privateClaims == nil {
			t.privateClaims = map[string]interface{}{}
		}
		t.privateClaims[name] = value
	}
	return nil
}

func (t *stdToken) Audience() []string {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.audience != nil {
		return t.audience.Get()
	}
	return nil
}

func (t *stdToken) Expiration() time.Time {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.expiration != nil {
		return t.expiration.Get()
	}
	return time.Time{}
}

func (t *stdToken) IssuedAt() time.Time {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.issuedAt != nil {
		return t.issuedAt.Get()
	}
	return time.Time{}
}

func (t *stdToken) Issuer() string {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.issuer != nil {
		return *(t.issuer)
	}
	return ""
}

func (t *stdToken) JwtID() string {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.jwtID != nil {
		return *(t.jwtID)
	}
	return ""
}

func (t *stdToken) NotBefore() time.Time {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.notBefore != nil {
		return t.notBefore.Get()
	}
	return time.Time{}
}

func (t *stdToken) Subject() string {
	t.mu.RLock()
	defer t.mu.RUnlock()
	if t.subject != nil {
		return *(t.subject)
	}
	return ""
}

func (t *stdToken) PrivateClaims() map[string]interface{} {
	t.mu.RLock()
	defer t.mu.RUnlock()
	return t.privateClaims
}

func (t *stdToken) makePairs() []*ClaimPair {
	t.mu.RLock()
	defer t.mu.RUnlock()

	var pairs []*ClaimPair
	if t.audience != nil {
		v := t.audience.Get()
		pairs = append(pairs, &ClaimPair{Key: AudienceKey, Value: v})
	}
	if t.expiration != nil {
		v := t.expiration.Get()
		pairs = append(pairs, &ClaimPair{Key: ExpirationKey, Value: v})
	}
	if t.issuedAt != nil {
		v := t.issuedAt.Get()
		pairs = append(pairs, &ClaimPair{Key: IssuedAtKey, Value: v})
	}
	if t.issuer != nil {
		v := *(t.issuer)
		pairs = append(pairs, &ClaimPair{Key: IssuerKey, Value: v})
	}
	if t.jwtID != nil {
		v := *(t.jwtID)
		pairs = append(pairs, &ClaimPair{Key: JwtIDKey, Value: v})
	}
	if t.notBefore != nil {
		v := t.notBefore.Get()
		pairs = append(pairs, &ClaimPair{Key: NotBeforeKey, Value: v})
	}
	if t.subject != nil {
		v := *(t.subject)
		pairs = append(pairs, &ClaimPair{Key: SubjectKey, Value: v})
	}
	for k, v := range t.privateClaims {
		pairs = append(pairs, &ClaimPair{Key: k, Value: v})
	}
	return pairs
}

func (t *stdToken) UnmarshalJSON(buf []byte) error {
	t.mu.Lock()
	defer t.mu.Unlock()
	t.audience = nil
	t.expiration = nil
	t.issuedAt = nil
	t.issuer = nil
	t.jwtID = nil
	t.notBefore = nil
	t.subject = nil
	dec := json.NewDecoder(bytes.NewReader(buf))
LOOP:
	for {
		tok, err := dec.Token()
		if err != nil {
			return errors.Wrap(err, `error reading token`)
		}
		switch tok := tok.(type) {
		case json.Delim:
			// Assuming we're doing everything correctly, we should ONLY
			// get either '{' or '}' here.
			if tok == '}' { // End of object
				break LOOP
			} else if tok != '{' {
				return errors.Errorf(`expected '{', but got '%c'`, tok)
			}
		case string: // Objects can only have string keys
			switch tok {
			case AudienceKey:
				var decoded types.StringList
				if err := dec.Decode(&decoded); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, AudienceKey)
				}
				t.audience = decoded
			case ExpirationKey:
				var decoded types.NumericDate
				if err := dec.Decode(&decoded); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, ExpirationKey)
				}
				t.expiration = &decoded
			case IssuedAtKey:
				var decoded types.NumericDate
				if err := dec.Decode(&decoded); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, IssuedAtKey)
				}
				t.issuedAt = &decoded
			case IssuerKey:
				if err := json.AssignNextStringToken(&t.issuer, dec); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, IssuerKey)
				}
			case JwtIDKey:
				if err := json.AssignNextStringToken(&t.jwtID, dec); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, JwtIDKey)
				}
			case NotBeforeKey:
				var decoded types.NumericDate
				if err := dec.Decode(&decoded); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, NotBeforeKey)
				}
				t.notBefore = &decoded
			case SubjectKey:
				if err := json.AssignNextStringToken(&t.subject, dec); err != nil {
					return errors.Wrapf(err, `failed to decode value for key %s`, SubjectKey)
				}
			default:
				decoded, err := registry.Decode(dec, tok)
				if err != nil {
					return err
				}
				t.setNoLock(tok, decoded)
			}
		default:
			return errors.Errorf(`invalid token %T`, tok)
		}
	}
	return nil
}

func (t stdToken) MarshalJSON() ([]byte, error) {
	t.mu.RLock()
	defer t.mu.RUnlock()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	data := make(map[string]interface{})
	fields := make([]string, 0, 7)
	for iter := t.Iterate(ctx); iter.Next(ctx); {
		pair := iter.Pair()
		fields = append(fields, pair.Key.(string))
		data[pair.Key.(string)] = pair.Value
	}

	sort.Strings(fields)
	buf := pool.GetBytesBuffer()
	defer pool.ReleaseBytesBuffer(buf)
	buf.WriteByte('{')
	enc := json.NewEncoder(buf)
	for i, f := range fields {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteRune('"')
		buf.WriteString(f)
		buf.WriteString(`":`)
		v := data[f]
		switch v := v.(type) {
		case []byte:
			buf.WriteRune('"')
			buf.WriteString(base64.EncodeToString(v))
			buf.WriteRune('"')
		case time.Time:
			switch f {
			case ExpirationKey, IssuedAtKey, NotBeforeKey:
				enc.Encode(v.Unix())
			default:
				if err := enc.Encode(v); err != nil {
					return nil, errors.Wrapf(err, `failed to marshal field %s`, f)
				}
				buf.Truncate(buf.Len() - 1)
			}
		default:
			if err := enc.Encode(v); err != nil {
				return nil, errors.Wrapf(err, `failed to marshal field %s`, f)
			}
			buf.Truncate(buf.Len() - 1)
		}
	}
	buf.WriteByte('}')
	ret := make([]byte, buf.Len())
	copy(ret, buf.Bytes())
	return ret, nil
}

func (t *stdToken) Iterate(ctx context.Context) Iterator {
	pairs := t.makePairs()
	ch := make(chan *ClaimPair, len(pairs))
	go func(ctx context.Context, ch chan *ClaimPair, pairs []*ClaimPair) {
		defer close(ch)
		for _, pair := range pairs {
			select {
			case <-ctx.Done():
				return
			case ch <- pair:
			}
		}
	}(ctx, ch, pairs)
	return mapiter.New(ch)
}

func (t *stdToken) Walk(ctx context.Context, visitor Visitor) error {
	return iter.WalkMap(ctx, t, visitor)
}

func (t *stdToken) AsMap(ctx context.Context) (map[string]interface{}, error) {
	return iter.AsMap(ctx, t)
}
