package codec

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func NewStringKeyCodec[T ~string]() NameableKeyCodec[T] { return stringKey[T]{} }

const (
	// StringDelimiter defines the delimiter of a string key when used in non-terminal encodings.
	StringDelimiter uint8 = 0x0
)

type stringKey[T ~string] struct{}

func (stringKey[T]) Encode(buffer []byte, key T) (int, error) {
	return copy(buffer, key), nil
}

func (stringKey[T]) Decode(buffer []byte) (int, T, error) {
	return len(buffer), T(buffer), nil
}

func (stringKey[T]) EncodeJSON(value T) ([]byte, error) {
	return json.Marshal(value)
}

func (stringKey[T]) DecodeJSON(b []byte) (T, error) {
	var value T
	err := json.Unmarshal(b, &value)
	return value, err
}

func (stringKey[T]) Size(key T) int {
	return len(key)
}

func (stringKey[T]) EncodeNonTerminal(buffer []byte, key T) (int, error) {
	for i := range key {
		c := key[i]
		if c == StringDelimiter {
			return 0, fmt.Errorf("%w: string is not allowed to have the string delimiter (%c) in non terminal encodings of strings", ErrEncoding, StringDelimiter)
		}
		buffer[i] = c
	}

	return len(key) + 1, nil
}

func (stringKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) {
	i := bytes.IndexByte(buffer, StringDelimiter)
	if i == -1 {
		return 0, "", fmt.Errorf("%w: not a valid non terminal buffer, no instances of the string delimiter %c found", ErrEncoding, StringDelimiter)
	}
	return i + 1, T(buffer[:i]), nil
}

func (stringKey[T]) SizeNonTerminal(key T) int { return len(key) + 1 }

func (stringKey[T]) Stringify(key T) string {
	return (string)(key)
}

func (stringKey[T]) KeyType() string {
	return "string"
}

func (s stringKey[T]) WithName(name string) KeyCodec[T] {
	return NamedKeyCodec[T]{KeyCodec: s, Name: name}
}
