package config

import (
	"fmt"

	"github.com/awnumar/memguard"
)

type protectedSecretImpl struct{}

func (*protectedSecretImpl) Container(secret []byte) secretContainer {
	return &protectedSecretContainer{
		enclave: memguard.NewEnclave(secret),
	}
}

func (*protectedSecretImpl) EmptyBuffer() SecretBuffer {
	return &lockedBuffer{}
}

func (*protectedSecretImpl) Wipe(secret []byte) {
	memguard.WipeBytes(secret)
}

type lockedBuffer struct {
	buf *memguard.LockedBuffer
}

func (lb *lockedBuffer) Size() int {
	if lb.buf == nil {
		return 0
	}
	return lb.buf.Size()
}

func (lb *lockedBuffer) Grow(capacity int) {
	size := lb.Size()
	if capacity <= size {
		return
	}

	buf := memguard.NewBuffer(capacity)
	if lb.buf != nil {
		buf.Copy(lb.buf.Bytes())
	}
	lb.buf.Destroy()
	lb.buf = buf
}

func (lb *lockedBuffer) Bytes() []byte {
	if lb.buf == nil {
		return nil
	}
	return lb.buf.Bytes()
}

func (lb *lockedBuffer) TemporaryString() string {
	if lb.buf == nil {
		return ""
	}
	return lb.buf.String()
}

func (lb *lockedBuffer) String() string {
	if lb.buf == nil {
		return ""
	}
	return string(lb.buf.Bytes())
}

func (lb *lockedBuffer) Destroy() {
	if lb.buf == nil {
		return
	}
	lb.buf.Destroy()
	lb.buf = nil
}

type protectedSecretContainer struct {
	enclave *memguard.Enclave
}

func (c *protectedSecretContainer) Destroy() {
	if c.enclave == nil {
		return
	}

	// Wipe the secret from memory
	lockbuf, err := c.enclave.Open()
	if err == nil {
		lockbuf.Destroy()
	}
	c.enclave = nil
}

func (c *protectedSecretContainer) Equals(ref []byte) (bool, error) {
	if c.enclave == nil {
		return false, nil
	}

	// Get a locked-buffer of the secret to perform the comparison
	lockbuf, err := c.enclave.Open()
	if err != nil {
		return false, fmt.Errorf("opening enclave failed: %w", err)
	}
	defer lockbuf.Destroy()

	return lockbuf.EqualTo(ref), nil
}

func (c *protectedSecretContainer) Buffer() (SecretBuffer, error) {
	if c.enclave == nil {
		return &lockedBuffer{}, nil
	}

	// Get a locked-buffer of the secret to perform the comparison
	lockbuf, err := c.enclave.Open()
	if err != nil {
		return nil, fmt.Errorf("opening enclave failed: %w", err)
	}

	return &lockedBuffer{lockbuf}, nil
}

func (*protectedSecretContainer) AsBuffer(secret []byte) SecretBuffer {
	return &lockedBuffer{memguard.NewBufferFromBytes(secret)}
}

func (c *protectedSecretContainer) Replace(secret []byte) {
	c.enclave = memguard.NewEnclave(secret)
}
