package collections

import (
	"bytes"
	"context"
	"errors"
	"fmt"

	"cosmossdk.io/collections/codec"
	store "cosmossdk.io/collections/corecompat"
)

// ErrInvalidIterator is returned when an Iterate call resulted in an invalid iterator.
var ErrInvalidIterator = errors.New("collections: invalid iterator")

// Order defines the key order.
type Order uint8

const (
	// OrderAscending instructs the Iterator to provide keys from the smallest to the greatest.
	OrderAscending Order = 0
	// OrderDescending instructs the Iterator to provide keys from the greatest to the smallest.
	OrderDescending Order = 1
)

type rangeKeyKind uint8

const (
	rangeKeyExact rangeKeyKind = iota
	rangeKeyNext
	rangeKeyPrefixEnd
)

// RangeKey wraps a generic range key K, acts as an enum which defines different
// ways to encode the wrapped key to bytes when it's being used in an iteration.
type RangeKey[K any] struct {
	kind rangeKeyKind
	key  K
}

// RangeKeyNext instantiates a RangeKey that when encoded to bytes
// identifies the next key after the provided key K.
// Example: given a string key "ABCD" the next key is bytes("ABCD\0")
// It's useful when defining inclusivity or exclusivity of a key
// in store iteration. Specifically: to make an Iterator start exclude key K
// I would return a RangeKeyNext(key) in the Ranger start.
func RangeKeyNext[K any](key K) *RangeKey[K] {
	return &RangeKey[K]{key: key, kind: rangeKeyNext}
}

// RangeKeyPrefixEnd instantiates a RangeKey that when encoded to bytes
// identifies the key that would end the prefix of the key K.
// Example: if the string key "ABCD" is provided, it would be encoded as bytes("ABCE").
func RangeKeyPrefixEnd[K any](key K) *RangeKey[K] {
	return &RangeKey[K]{key: key, kind: rangeKeyPrefixEnd}
}

// RangeKeyExact instantiates a RangeKey that applies no modifications
// to the key K. So its bytes representation will not be altered.
func RangeKeyExact[K any](key K) *RangeKey[K] {
	return &RangeKey[K]{key: key, kind: rangeKeyExact}
}

// Ranger defines a generic interface that provides a range of keys.
type Ranger[K any] interface {
	// RangeValues is defined by Ranger implementers.
	// The implementer can optionally return a start and an end.
	// If start is nil and end is not, the iteration will include all the keys
	// in the collection up until the provided end.
	// If start is defined and end is nil, the iteration will include all the keys
	// in the collection starting from the provided start.
	// If both are nil then the iteration will include all the possible keys in the
	// collection.
	// Order defines the order of the iteration, if order is OrderAscending then the
	// iteration will yield keys from the smallest to the biggest, if order
	// is OrderDescending then the iteration will yield keys from the biggest to the smallest.
	// Ordering is defined by the keys bytes representation, which is dependent on the KeyCodec used.
	RangeValues() (start, end *RangeKey[K], order Order, err error)
}

// Range is a Ranger implementer.
type Range[K any] struct {
	start *RangeKey[K]
	end   *RangeKey[K]
	order Order
}

// Prefix sets a fixed prefix for the key range.
func (r *Range[K]) Prefix(key K) *Range[K] {
	r.start = RangeKeyExact(key)
	r.end = RangeKeyPrefixEnd(key)
	return r
}

// StartInclusive makes the range contain only keys which are bigger or equal to the provided start K.
func (r *Range[K]) StartInclusive(start K) *Range[K] {
	r.start = RangeKeyExact(start)
	return r
}

// StartExclusive makes the range contain only keys which are bigger to the provided start K.
func (r *Range[K]) StartExclusive(start K) *Range[K] {
	r.start = RangeKeyNext(start)
	return r
}

// EndInclusive makes the range contain only keys which are smaller or equal to the provided end K.
func (r *Range[K]) EndInclusive(end K) *Range[K] {
	r.end = RangeKeyNext(end)
	return r
}

// EndExclusive makes the range contain only keys which are smaller to the provided end K.
func (r *Range[K]) EndExclusive(end K) *Range[K] {
	r.end = RangeKeyExact(end)
	return r
}

func (r *Range[K]) Descending() *Range[K] {
	r.order = OrderDescending
	return r
}

// test sentinel error
var (
	errOrder = errors.New("collections: invalid order")
)

func (r *Range[K]) RangeValues() (start, end *RangeKey[K], order Order, err error) {
	return r.start, r.end, r.order, nil
}

// parseRangeInstruction converts a Ranger into start bytes, end bytes and order of a store iteration.
func parseRangeInstruction[K any](prefix []byte, keyCodec codec.KeyCodec[K], r Ranger[K]) ([]byte, []byte, Order, error) {
	var (
		start *RangeKey[K]
		end   *RangeKey[K]
		order = OrderAscending
		err   error
	)

	if r != nil {
		start, end, order, err = r.RangeValues()
		if err != nil {
			return nil, nil, 0, err
		}
	}

	startBytes := prefix
	if start != nil {
		startBytes, err = encodeRangeBound(prefix, keyCodec, start)
		if err != nil {
			return nil, nil, 0, err
		}
	}
	var endBytes []byte
	if end != nil {
		endBytes, err = encodeRangeBound(prefix, keyCodec, end)
		if err != nil {
			return nil, nil, 0, err
		}
	} else {
		endBytes = nextBytesPrefixKey(prefix)
	}
	if bytes.Compare(startBytes, endBytes) == 1 {
		return nil, nil, 0, ErrInvalidIterator
	}
	return startBytes, endBytes, order, nil
}

// iteratorFromRanger generates an Iterator instance, with the proper prefixing and ranging.
// a nil Ranger can be seen as an ascending iteration over all the possible keys.
func iteratorFromRanger[K, V any](ctx context.Context, m Map[K, V], r Ranger[K]) (iter Iterator[K, V], err error) {
	startBytes, endBytes, order, err := parseRangeInstruction(m.prefix, m.kc, r)
	if err != nil {
		return Iterator[K, V]{}, err
	}
	return newIterator(ctx, startBytes, endBytes, order, m)
}

func newIterator[K, V any](ctx context.Context, start, end []byte, order Order, m Map[K, V]) (Iterator[K, V], error) {
	kv := m.sa(ctx)
	var (
		iter store.Iterator
		err  error
	)
	switch order {
	case OrderAscending:
		iter, err = kv.Iterator(start, end)
	case OrderDescending:
		iter, err = kv.ReverseIterator(start, end)
	default:
		return Iterator[K, V]{}, errOrder
	}
	if err != nil {
		return Iterator[K, V]{}, err
	}

	return Iterator[K, V]{
		kc:           m.kc,
		vc:           m.vc,
		iter:         iter,
		prefixLength: len(m.prefix),
	}, nil
}

// Iterator defines a generic wrapper around a storetypes.Iterator.
// This iterator provides automatic key and value encoding,
// it assumes all the keys and values contained within the storetypes.Iterator
// range are the same.
type Iterator[K, V any] struct {
	kc codec.KeyCodec[K]
	vc codec.ValueCodec[V]

	iter store.Iterator

	prefixLength int // prefixLength refers to the bytes provided by Prefix.Bytes, not Ranger.RangeValues() prefix.
}

// Value returns the current iterator value bytes decoded.
func (i Iterator[K, V]) Value() (V, error) {
	return i.vc.Decode(i.iter.Value())
}

// Key returns the current storetypes.Iterator decoded key.
func (i Iterator[K, V]) Key() (K, error) {
	bytesKey := i.iter.Key()[i.prefixLength:] // strip prefix namespace

	read, key, err := i.kc.Decode(bytesKey)
	if err != nil {
		var k K
		return k, err
	}
	if read != len(bytesKey) {
		var k K
		return k, fmt.Errorf("%w: key decoder didn't fully consume the key: %T %x %d", ErrEncoding, i.kc, bytesKey, read)
	}
	return key, nil
}

// Values fully consumes the iterator and returns all the decoded values contained within the range.
func (i Iterator[K, V]) Values() ([]V, error) {
	defer i.Close()

	var values []V
	for ; i.iter.Valid(); i.iter.Next() {
		value, err := i.Value()
		if err != nil {
			return nil, err
		}
		values = append(values, value)
	}
	return values, nil
}

// Keys fully consumes the iterator and returns all the decoded keys contained within the range.
func (i Iterator[K, V]) Keys() ([]K, error) {
	defer i.Close()

	var keys []K
	for ; i.iter.Valid(); i.iter.Next() {
		key, err := i.Key()
		if err != nil {
			return nil, err
		}
		keys = append(keys, key)
	}
	return keys, nil
}

// KeyValue returns the current key and value decoded.
func (i Iterator[K, V]) KeyValue() (kv KeyValue[K, V], err error) {
	key, err := i.Key()
	if err != nil {
		return kv, err
	}
	value, err := i.Value()
	if err != nil {
		return kv, err
	}
	kv.Key = key
	kv.Value = value
	return kv, nil
}

// KeyValues fully consumes the iterator and returns the list of key and values within the iterator range.
func (i Iterator[K, V]) KeyValues() ([]KeyValue[K, V], error) {
	defer i.Close()

	var kvs []KeyValue[K, V]
	for ; i.iter.Valid(); i.iter.Next() {
		kv, err := i.KeyValue()
		if err != nil {
			return nil, err
		}
		kvs = append(kvs, kv)
	}

	return kvs, nil
}

func (i Iterator[K, V]) Close() error { return i.iter.Close() }
func (i Iterator[K, V]) Next()        { i.iter.Next() }
func (i Iterator[K, V]) Valid() bool  { return i.iter.Valid() }

// KeyValue represent a Key and Value pair of an iteration.
type KeyValue[K, V any] struct {
	Key   K
	Value V
}

// encodeRangeBound encodes a range bound, modifying the key bytes to adhere to bound semantics.
func encodeRangeBound[T any](prefix []byte, keyCodec codec.KeyCodec[T], bound *RangeKey[T]) ([]byte, error) {
	key, err := EncodeKeyWithPrefix(prefix, keyCodec, bound.key)
	if err != nil {
		return nil, err
	}
	switch bound.kind {
	case rangeKeyExact:
		return key, nil
	case rangeKeyNext:
		return nextBytesKey(key), nil
	case rangeKeyPrefixEnd:
		return nextBytesPrefixKey(key), nil
	default:
		panic("undefined bound kind")
	}
}

// nextBytesKey returns the next byte key after this one.
func nextBytesKey(b []byte) []byte {
	return append(b, 0)
}

// nextBytesPrefixKey returns the []byte that would end a
// range query for all []byte with a certain prefix
// Deals with last byte of prefix being FF without overflowing
func nextBytesPrefixKey(prefix []byte) []byte {
	if len(prefix) == 0 {
		return nil
	}

	end := make([]byte, len(prefix))
	copy(end, prefix)

	for {
		if end[len(end)-1] != byte(255) {
			end[len(end)-1]++
			break
		}

		end = end[:len(end)-1]

		if len(end) == 0 {
			end = nil
			break
		}
	}

	return end
}
