package obfuscated2

import (
	"crypto/cipher"
	"encoding/binary"
	"io"

	"github.com/go-faster/errors"

	"github.com/gotd/td/internal/crypto"
)

type keys struct {
	header  []byte
	encrypt cipher.Stream
	decrypt cipher.Stream
}

func (k *keys) createStreams(init, secret []byte) error {
	// preallocate 256 bit key + 16 byte secret
	const keyLength = 32 + 16

	encryptKey := append(make([]byte, 0, keyLength), init[8:40]...)
	encryptIV := append(make([]byte, 0, 16), init[40:56]...)

	initRev := getDecryptInit(init)
	decryptKey := append(make([]byte, 0, keyLength), initRev[:32]...)
	decryptIV := append(make([]byte, 0, 16), initRev[32:48]...)

	if len(secret) > 0 {
		if len(secret) < 16 {
			return errors.Errorf("invalid secret size %d", len(secret))
		}
		secret = secret[0:16]

		encryptKey = crypto.SHA256(encryptKey, secret)
		decryptKey = crypto.SHA256(decryptKey, secret)
	}

	var err error
	k.encrypt, err = createCTR(encryptKey, encryptIV)
	if err != nil {
		return err
	}

	k.decrypt, err = createCTR(decryptKey, decryptIV)
	if err != nil {
		return err
	}

	return nil
}

func generateKeys(randSource io.Reader, protocol [4]byte, secret []byte, dc int) (keys, error) {
	init, err := generateInit(randSource)
	if err != nil {
		return keys{}, err
	}

	var k keys
	if err := k.createStreams(init[:], secret); err != nil {
		return keys{}, err
	}

	copy(init[56:60], protocol[:])
	binary.LittleEndian.PutUint16(init[60:62], uint16(dc))

	var encryptedInit [64]byte
	k.encrypt.XORKeyStream(encryptedInit[:], init[:])
	k.header = make([]byte, 64)
	copy(k.header, init[0:56])
	copy(k.header[56:], encryptedInit[56:56+8])

	return k, nil
}
