package app

import (
	
	

	
)

// BrowserStorage defines an interface for interacting with web browser storage
// mechanisms (such as localStorage in the Web API). It provides methods to set,
// get, delete, and iterate over stored items, among other functionalities.
type BrowserStorage interface {
	// Set stores a value associated with a given key. The value must be capable
	// of being converted to JSON format. If the value cannot be converted to
	// JSON or if there's an issue with storage, an error is returned.
	Set(k string, v any) error

	// Get retrieves the value associated with a given key and stores it into
	// the provided variable v. The variable v must be a pointer to a type that
	// is compatible with the stored value. If v is not a pointer, an error is
	// returned, indicating incorrect usage.
	//
	// If the key does not exist, the operation performs no action on v, leaving
	// it unchanged. Use Contains to check whether a key exists.
	Get(k string, v any) error

	// Del removes the item associated with the specified key from the storage.
	// If the key does not exist, the operation is a no-op.
	Del(k string)

	// Len returns the total number of items currently stored. This count
	// includes all keys, regardless of their value.
	Len() int

	// Clear removes all items from the storage, effectively resetting it.
	Clear()

	// ForEach iterates over each item in the storage, executing the provided
	// function f for each key. The exact order of iteration is not guaranteed
	// and may vary across different implementations.
	ForEach(f func(k string))

	// Contains checks if the storage contains an item associated with the given
	// key. It returns true if the item exists, false otherwise. This method
	// provides a way to check for the existence of a key without retrieving the
	// associated value.
	Contains(k string) bool
}

type memoryStorage struct {
	mu   sync.RWMutex
	data map[string][]byte
}

func newMemoryStorage() *memoryStorage {
	return &memoryStorage{
		data: make(map[string][]byte),
	}
}

func ( *memoryStorage) ( string,  any) error {
	,  := json.Marshal()
	if  != nil {
		return 
	}

	.mu.Lock()
	.data[] = 
	.mu.Unlock()
	return nil
}

func ( *memoryStorage) ( string,  any) error {
	.mu.RLock()
	,  := .data[]
	if ! {
		.mu.RUnlock()
		return nil
	}

	.mu.RUnlock()
	return json.Unmarshal(, )
}

func ( *memoryStorage) ( string) {
	.mu.Lock()
	delete(.data, )
	.mu.Unlock()
}

func ( *memoryStorage) () {
	.mu.Lock()
	for  := range .data {
		delete(.data, )
	}
	.mu.Unlock()
}

func ( *memoryStorage) () int {
	.mu.RLock()
	 := len(.data)
	.mu.RUnlock()
	return 
}

func ( *memoryStorage) ( func( string)) {
	for  := range .data {
		()
	}
}

func ( *memoryStorage) ( string) bool {
	,  := .data[]
	return 
}

type jsStorage struct {
	name  string
	mutex sync.RWMutex
}

func newJSStorage( string) *jsStorage {
	return &jsStorage{name: }
}

func ( *jsStorage) ( string,  any) ( error) {
	defer func() {
		 := recover()
		if  != nil {
			 = errors.New("setting storage value failed").
				WithTag("storage-type", .name).
				WithTag("key", ).
				Wrap(.(error))
		}
	}()

	.mutex.Lock()
	defer .mutex.Unlock()

	,  := json.Marshal()
	if  != nil {
		return 
	}

	Window().Get(.name).Call("setItem", , string())
	return nil
}

func ( *jsStorage) ( string,  any) error {
	.mutex.RLock()
	defer .mutex.RUnlock()

	 := Window().Get(.name).Call("getItem", )
	if .IsNull() {
		return nil
	}

	return json.Unmarshal([]byte(.String()), )
}

func ( *jsStorage) ( string) {
	.mutex.Lock()
	defer .mutex.Unlock()
	Window().Get(.name).Call("removeItem", )
}

func ( *jsStorage) () {
	.mutex.Lock()
	defer .mutex.Unlock()
	Window().Get(.name).Call("clear")
}

func ( *jsStorage) () int {
	.mutex.Lock()
	defer .mutex.Unlock()
	return .len()
}

func ( *jsStorage) () int {
	return Window().Get(.name).Get("length").Int()
}

func ( *jsStorage) ( func( string)) {
	.mutex.Lock()
	 := .len()
	 := make(map[string]struct{}, )
	for  := 0;  < ; ++ {
		 := Window().Get(.name).Call("key", )
		if .Truthy() {
			[.String()] = struct{}{}
		}
	}
	.mutex.Unlock()

	for  := range  {
		()
	}
}

func ( *jsStorage) ( string) bool {
	.mutex.Lock()
	defer .mutex.Unlock()
	return !Window().Get(.name).Call("getItem", ).IsNull()
}