package Seeder

import (
	"encoding/base64"

	"git.tovijaeschke.xyz/tovi/Capsule/Backend/Database"
	"git.tovijaeschke.xyz/tovi/Capsule/Backend/Models"

	"github.com/gofrs/uuid"
)

func seedMessage(
	primaryUser, secondaryUser Models.User,
	primaryUserAssociationKey, secondaryUserAssociationKey string,
	i int,
) error {
	var (
		message            Models.Message
		messageData        Models.MessageData
		key, userKey       AesKey
		keyCiphertext      []byte
		plaintext          string
		dataCiphertext     []byte
		senderIDCiphertext []byte
		err                error
	)

	plaintext = "Test Message"

	userKey, err = GenerateAesKey()
	if err != nil {
		panic(err)
	}

	key, err = GenerateAesKey()
	if err != nil {
		panic(err)
	}

	dataCiphertext, err = key.AesEncrypt([]byte(plaintext))
	if err != nil {
		panic(err)
	}

	senderIDCiphertext, err = key.AesEncrypt([]byte(primaryUser.ID.String()))
	if err != nil {
		panic(err)
	}

	if i%2 == 0 {
		senderIDCiphertext, err = key.AesEncrypt([]byte(secondaryUser.ID.String()))
		if err != nil {
			panic(err)
		}
	}

	keyCiphertext, err = userKey.AesEncrypt(
		[]byte(base64.StdEncoding.EncodeToString(key.Key)),
	)
	if err != nil {
		panic(err)
	}

	messageData = Models.MessageData{
		Data:         base64.StdEncoding.EncodeToString(dataCiphertext),
		SenderID:     base64.StdEncoding.EncodeToString(senderIDCiphertext),
		SymmetricKey: base64.StdEncoding.EncodeToString(keyCiphertext),
	}

	message = Models.Message{
		MessageData: messageData,
		SymmetricKey: base64.StdEncoding.EncodeToString(
			EncryptWithPublicKey(userKey.Key, decodedPublicKey),
		),
		AssociationKey: primaryUserAssociationKey,
	}

	err = Database.CreateMessage(&message)
	if err != nil {
		return err
	}

	message = Models.Message{
		MessageData: messageData,
		SymmetricKey: base64.StdEncoding.EncodeToString(
			EncryptWithPublicKey(userKey.Key, decodedPublicKey),
		),
		AssociationKey: secondaryUserAssociationKey,
	}

	return Database.CreateMessage(&message)
}

func seedConversationDetail(key AesKey) (Models.ConversationDetail, error) {
	var (
		messageThread     Models.ConversationDetail
		name              string
		nameCiphertext    []byte
		twoUserCiphertext []byte
		err               error
	)

	name = "Test Conversation"

	nameCiphertext, err = key.AesEncrypt([]byte(name))
	if err != nil {
		panic(err)
	}

	twoUserCiphertext, err = key.AesEncrypt([]byte("false"))
	if err != nil {
		panic(err)
	}

	messageThread = Models.ConversationDetail{
		Name:    base64.StdEncoding.EncodeToString(nameCiphertext),
		TwoUser: base64.StdEncoding.EncodeToString(twoUserCiphertext),
	}

	err = Database.CreateConversationDetail(&messageThread)
	return messageThread, err
}

func seedUserConversation(
	user Models.User,
	threadID uuid.UUID,
	key AesKey,
) (Models.UserConversation, error) {
	var (
		messageThreadUser              Models.UserConversation
		conversationDetailIDCiphertext []byte
		adminCiphertext                []byte
		err                            error
	)

	conversationDetailIDCiphertext, err = key.AesEncrypt([]byte(threadID.String()))
	if err != nil {
		return messageThreadUser, err
	}

	adminCiphertext, err = key.AesEncrypt([]byte("true"))
	if err != nil {
		return messageThreadUser, err
	}

	messageThreadUser = Models.UserConversation{
		UserID:               user.ID,
		ConversationDetailID: base64.StdEncoding.EncodeToString(conversationDetailIDCiphertext),
		Admin:                base64.StdEncoding.EncodeToString(adminCiphertext),
		SymmetricKey: base64.StdEncoding.EncodeToString(
			EncryptWithPublicKey(key.Key, decodedPublicKey),
		),
	}

	err = Database.CreateUserConversation(&messageThreadUser)
	return messageThreadUser, err
}

func seedConversationDetailUser(
	user Models.User,
	conversationDetail Models.ConversationDetail,
	associationKey uuid.UUID,
	admin bool,
	key AesKey,
) (Models.ConversationDetailUser, error) {
	var (
		conversationDetailUser Models.ConversationDetailUser

		userIDCiphertext         []byte
		usernameCiphertext       []byte
		adminCiphertext          []byte
		associationKeyCiphertext []byte
		publicKeyCiphertext      []byte

		adminString = "false"

		err error
	)

	if admin {
		adminString = "true"
	}

	userIDCiphertext, err = key.AesEncrypt([]byte(user.ID.String()))
	if err != nil {
		return conversationDetailUser, err
	}

	usernameCiphertext, err = key.AesEncrypt([]byte(user.Username))
	if err != nil {
		return conversationDetailUser, err
	}

	adminCiphertext, err = key.AesEncrypt([]byte(adminString))
	if err != nil {
		return conversationDetailUser, err
	}

	associationKeyCiphertext, err = key.AesEncrypt([]byte(associationKey.String()))
	if err != nil {
		return conversationDetailUser, err
	}

	publicKeyCiphertext, err = key.AesEncrypt([]byte(user.AsymmetricPublicKey))
	if err != nil {
		return conversationDetailUser, err
	}

	conversationDetailUser = Models.ConversationDetailUser{
		ConversationDetailID: conversationDetail.ID,
		UserID:               base64.StdEncoding.EncodeToString(userIDCiphertext),
		Username:             base64.StdEncoding.EncodeToString(usernameCiphertext),
		Admin:                base64.StdEncoding.EncodeToString(adminCiphertext),
		AssociationKey:       base64.StdEncoding.EncodeToString(associationKeyCiphertext),
		PublicKey:            base64.StdEncoding.EncodeToString(publicKeyCiphertext),
	}

	err = Database.CreateConversationDetailUser(&conversationDetailUser)

	return conversationDetailUser, err
}

// SeedMessages seeds messages & conversations for testing
func SeedMessages() {
	var (
		conversationDetail          Models.ConversationDetail
		key                         AesKey
		primaryUser                 Models.User
		primaryUserAssociationKey   uuid.UUID
		secondaryUser               Models.User
		secondaryUserAssociationKey uuid.UUID
		i                           int
		err                         error
	)

	key, err = GenerateAesKey()
	if err != nil {
		panic(err)
	}
	conversationDetail, err = seedConversationDetail(key)

	primaryUserAssociationKey, err = uuid.NewV4()
	if err != nil {
		panic(err)
	}
	secondaryUserAssociationKey, err = uuid.NewV4()
	if err != nil {
		panic(err)
	}

	primaryUser, err = Database.GetUserByUsername("testUser")
	if err != nil {
		panic(err)
	}

	_, err = seedUserConversation(
		primaryUser,
		conversationDetail.ID,
		key,
	)
	if err != nil {
		panic(err)
	}

	secondaryUser, err = Database.GetUserByUsername("ATestUser2")
	if err != nil {
		panic(err)
	}

	_, err = seedUserConversation(
		secondaryUser,
		conversationDetail.ID,
		key,
	)
	if err != nil {
		panic(err)
	}

	_, err = seedConversationDetailUser(
		primaryUser,
		conversationDetail,
		primaryUserAssociationKey,
		true,
		key,
	)
	if err != nil {
		panic(err)
	}

	_, err = seedConversationDetailUser(
		secondaryUser,
		conversationDetail,
		secondaryUserAssociationKey,
		false,
		key,
	)
	if err != nil {
		panic(err)
	}

	for i = 0; i <= 100; i++ {
		err = seedMessage(
			primaryUser,
			secondaryUser,
			primaryUserAssociationKey.String(),
			secondaryUserAssociationKey.String(),
			i,
		)
		if err != nil {
			panic(err)
		}
	}
}
