package protocol

import (
	"crypto/ecdsa"
	crand "crypto/rand"
	"errors"

	"github.com/status-im/status-go/protocol/common"
	"github.com/status-im/status-go/protocol/protobuf"
)

var ErrCipherMessageAutentificationFailed = "cipher: message authentication failed"

func EncryptIdentityImagesWithContactPubKeys(iis map[string]*protobuf.IdentityImage, m *Messenger) (err error) {
	// Make AES key
	AESKey := make([]byte, 32)
	_, err = crand.Read(AESKey)
	if err != nil {
		return err
	}

	for _, ii := range iis {
		// Encrypt image payload with the AES key
		var encryptedPayload []byte
		encryptedPayload, err = common.Encrypt(ii.Payload, AESKey, crand.Reader)
		if err != nil {
			return err
		}

		// Overwrite the unencrypted payload with the newly encrypted payload
		ii.Payload = encryptedPayload
		ii.Encrypted = true
		m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
			if !contact.added() {
				return true
			}
			var pubK *ecdsa.PublicKey
			var sharedKey []byte
			var eAESKey []byte

			pubK, err = contact.PublicKey()
			if err != nil {
				return false
			}
			// Generate a Diffie-Helman (DH) between the sender private key and the recipient's public key
			sharedKey, err = common.MakeECDHSharedKey(m.identity, pubK)
			if err != nil {
				return false
			}

			// Encrypt the main AES key with AES encryption using the DH key
			eAESKey, err = common.Encrypt(AESKey, sharedKey, crand.Reader)
			if err != nil {
				return false
			}

			// Append the the encrypted main AES key to the IdentityImage's EncryptionKeys slice.
			ii.EncryptionKeys = append(ii.EncryptionKeys, eAESKey)
			return true
		})
		if err != nil {
			return err
		}
	}

	return nil
}

func DecryptIdentityImagesWithIdentityPrivateKey(iis map[string]*protobuf.IdentityImage, recipientIdentity *ecdsa.PrivateKey, senderPubKey *ecdsa.PublicKey) error {
image:
	for _, ii := range iis {
		for _, empk := range ii.EncryptionKeys {
			// Generate a Diffie-Helman (DH) between the recipient's private key and the sender's public key
			sharedKey, err := common.MakeECDHSharedKey(recipientIdentity, senderPubKey)
			if err != nil {
				return err
			}

			// Decrypt the main encryption AES key with AES encryption using the DH key
			dAESKey, err := common.Decrypt(empk, sharedKey)
			if err != nil {
				if err.Error() == ErrCipherMessageAutentificationFailed {
					continue
				}
				return err
			}
			if dAESKey == nil {
				return errors.New("decrypting the payload encryption key resulted in no error and a nil key")
			}

			// Decrypt the payload with the newly decrypted main encryption AES key
			payload, err := common.Decrypt(ii.Payload, dAESKey)
			if err != nil {
				return err
			}
			if payload == nil {
				// TODO should this be a logger warn? A payload could theoretically be validly empty
				return errors.New("decrypting the payload resulted in no error and a nil payload")
			}

			// Overwrite the payload with the decrypted data
			ii.Payload = payload
			ii.Encrypted = false
			continue image
		}
	}

	return nil
}