package crypto

import (
	"agent/commons/utils"
	"agent/defines/derrs"
)

func NewXorCipher(key any) (ICipher, error) {
	cipher := &xor{}
	if err := cipher.SetKey(key); err != nil {
		return nil, err
	}
	return cipher, nil
}

type xor struct {
	key []byte

	encryptIndex int64
	decryptIndex int64
}

func (c *xor) New(key any) ICipher {
	cipher := &xor{}
	if err := cipher.SetKey(key); err != nil {
		panic(err)
	}
	return cipher
}

func (c *xor) SetKey(key any) (err error) {
	if c.key, err = any2key(key); err != nil {
		return err
	}
	c.encryptIndex = 0
	c.decryptIndex = 0
	return nil
}

// Encrypt шифрует данные. Поддерживает расшифровку в исходный буфер. Хотя буффер может быть любого размера, кратного
// еденице, все же рекомендуется использовать input размером кратным c.PlainBlockSize().
func (c *xor) Encrypt(input []byte, output []byte) (int, error) {
	outputSize := c.EncryptOutputSize(len(input))
	if len(output) < outputSize {
		return outputSize, derrs.NewIncorrectParamsError("len(output) < outputSize")
	}

	size := int64(len(c.key))

	for j, x := range input {
		output[j] = x ^ c.key[(c.encryptIndex+int64(j))%size]
	}

	c.encryptIndex += int64(outputSize)

	return outputSize, nil
}

func (c *xor) Decrypt(input []byte, output []byte) (int, error) {
	outputSize := c.DecryptOutputSize(len(input))
	if len(output) < outputSize {
		return outputSize, derrs.NewIncorrectParamsError("len(output) < outputSize")
	}

	size := int64(len(c.key))

	for j, x := range input {
		output[j] = x ^ c.key[(c.decryptIndex+int64(j))%size]
	}

	c.decryptIndex += int64(outputSize)

	return outputSize, nil
}

func (c *xor) EncryptOutputSize(inputSize int) int {
	return utils.RoundTo(inputSize, c.BlockSize())
	//return inputSize
}

func (c *xor) DecryptOutputSize(inputSize int) int {
	return utils.RoundTo(inputSize, c.BlockSize())
	//return inputSize
}

func (c *xor) BlockSize() int {
	return len(c.key)
}

func any2key(key any) (res []byte, err error) {
	switch x := key.(type) {
	case []byte:
		res = x
	case byte:
		res = []byte{x}
	case string:
		res = []byte(x)
	default:
		return nil, derrs.NewIncorrectParamsError("unsupported key type")
	}
	return res, nil
}
