package queue

import (
	"crypto/sha3"
	"encoding/base64"
	"fmt"
	"io"

	"github.com/go-json-experiment/json/jsontext"
)

// sender is an obfuscated sender ID.
//
// It acts approximately as a quotient type: since it is unexported, the only
// way to obtain values of type sender without unusual effort is to use the
// constructors provided by this package.
// This helps to prevent misuse that exposes actual user IDs.
type sender [28]byte

// Sender constructs an obfuscated sender ID.
// The salt must be shared by all ingesters.
// While the salt may be of any length, a length between 16 and 64 bytes is suggested.
func Sender(salt []byte, channel, user string) sender {
	b := make([]byte, 128)
	b = append(b, channel...)
	b = append(b, 0)
	b = append(b, salt...)
	b = append(b, 0)
	b = append(b, user...)
	return sender(sha3.Sum224(b))
}

func (s sender) String() string {
	b := base64.RawStdEncoding.AppendEncode(make([]byte, 0, 38), s[:])
	return string(b)
}

func (s sender) MarshalJSONTo(e *jsontext.Encoder) error {
	return e.WriteToken(jsontext.String(s.String()))
}

func (s *sender) UnmarshalJSONFrom(d *jsontext.Decoder) error {
	t, err := d.ReadToken()
	switch err {
	case nil: // do nothing
	case io.EOF:
		return io.ErrUnexpectedEOF
	default:
		return err
	}
	e := t.String()
	if t.Kind() != '"' {
		return fmt.Errorf("invalid token for sender %q", e)
	}
	if len(e) != 38 {
		return fmt.Errorf("invalid string for sender %q", e)
	}
	b, err := base64.RawStdEncoding.AppendDecode(make([]byte, 0, 28), []byte(e))
	if err != nil {
		return err
	}
	*s = sender(b)
	return nil
}