// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.

package objstore

import (
	
	
	
	
	
	
	

	
)

var errNotFound = errors.New("inmem: object not found")

// InMemBucket implements the objstore.Bucket interfaces against local memory.
// Methods from Bucket interface are thread-safe. Objects are assumed to be immutable.
type InMemBucket struct {
	mtx     sync.RWMutex
	objects map[string][]byte
	attrs   map[string]ObjectAttributes
}

// NewInMemBucket returns a new in memory Bucket.
// NOTE: Returned bucket is just a naive in memory bucket implementation. For test use cases only.
func () *InMemBucket {
	return &InMemBucket{
		objects: map[string][]byte{},
		attrs:   map[string]ObjectAttributes{},
	}
}

// Objects returns a copy of the internally stored objects.
// NOTE: For assert purposes.
func ( *InMemBucket) () map[string][]byte {
	.mtx.RLock()
	defer .mtx.RUnlock()

	 := make(map[string][]byte)
	for ,  := range .objects {
		[] = 
	}

	return 
}

// Iter calls f for each entry in the given directory. The argument to f is the full
// object name including the prefix of the inspected directory.
func ( *InMemBucket) ( context.Context,  string,  func(string) error,  ...IterOption) error {
	 := map[string]struct{}{}
	 := ApplyIterOptions(...)

	var  int
	 := strings.SplitAfter(, DirDelim)
	for ,  := range  {
		if  == "" {
			continue
		}
		++
	}

	.mtx.RLock()
	for  := range .objects {
		if !strings.HasPrefix(, ) ||  ==  {
			continue
		}

		if .Recursive {
			// Any object matching the prefix should be included.
			[] = struct{}{}
			continue
		}

		 := strings.SplitAfter(, DirDelim)
		[strings.Join([:+1], "")] = struct{}{}
	}
	.mtx.RUnlock()

	var  []string
	for  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		if strings.HasSuffix([], DirDelim) && strings.HasSuffix([], DirDelim) {
			return strings.Compare([], []) < 0
		}
		if strings.HasSuffix([], DirDelim) {
			return false
		}
		if strings.HasSuffix([], DirDelim) {
			return true
		}

		return strings.Compare([], []) < 0
	})

	for ,  := range  {
		if  := ();  != nil {
			return 
		}
	}
	return nil
}

// Get returns a reader for the given object name.
func ( *InMemBucket) ( context.Context,  string) (io.ReadCloser, error) {
	if  == "" {
		return nil, errors.New("inmem: object name is empty")
	}

	.mtx.RLock()
	,  := .objects[]
	.mtx.RUnlock()
	if ! {
		return nil, errNotFound
	}

	return io.NopCloser(bytes.NewReader()), nil
}

// GetRange returns a new range reader for the given object name and range.
func ( *InMemBucket) ( context.Context,  string, ,  int64) (io.ReadCloser, error) {
	if  == "" {
		return nil, errors.New("inmem: object name is empty")
	}

	.mtx.RLock()
	,  := .objects[]
	.mtx.RUnlock()
	if ! {
		return nil, errNotFound
	}

	if int64(len()) <  {
		return io.NopCloser(bytes.NewReader(nil)), nil
	}

	if  == -1 {
		return io.NopCloser(bytes.NewReader([:])), nil
	}

	if  <= 0 {
		return io.NopCloser(bytes.NewReader(nil)), errors.New("length cannot be smaller or equal 0")
	}

	if int64(len()) <= + {
		// Just return maximum of what we have.
		 = int64(len()) - 
	}

	return io.NopCloser(bytes.NewReader([ : +])), nil
}

// Exists checks if the given directory exists in memory.
func ( *InMemBucket) ( context.Context,  string) (bool, error) {
	.mtx.RLock()
	defer .mtx.RUnlock()
	,  := .objects[]
	return , nil
}

// Attributes returns information about the specified object.
func ( *InMemBucket) ( context.Context,  string) (ObjectAttributes, error) {
	.mtx.RLock()
	,  := .attrs[]
	.mtx.RUnlock()
	if ! {
		return ObjectAttributes{}, errNotFound
	}
	return , nil
}

// Upload writes the file specified in src to into the memory.
func ( *InMemBucket) ( context.Context,  string,  io.Reader) error {
	.mtx.Lock()
	defer .mtx.Unlock()
	,  := io.ReadAll()
	if  != nil {
		return 
	}
	.objects[] = 
	.attrs[] = ObjectAttributes{
		Size:         int64(len()),
		LastModified: time.Now(),
	}
	return nil
}

// Delete removes all data prefixed with the dir.
func ( *InMemBucket) ( context.Context,  string) error {
	.mtx.Lock()
	defer .mtx.Unlock()
	if ,  := .objects[]; ! {
		return errNotFound
	}
	delete(.objects, )
	delete(.attrs, )
	return nil
}

// IsObjNotFoundErr returns true if error means that object is not found. Relevant to Get operations.
func ( *InMemBucket) ( error) bool {
	return errors.Is(, errNotFound)
}

// IsAccessDeniedErr returns true if access to object is denied.
func ( *InMemBucket) ( error) bool {
	return false
}

func ( *InMemBucket) () error { return nil }

// Name returns the bucket name.
func ( *InMemBucket) () string {
	return "inmem"
}