///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC                                          //
//                                                                           //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file                                                              //
///////////////////////////////////////////////////////////////////////////////

package api

import (
	"github.com/pkg/errors"
	"gitlab.com/elixxir/crypto/fastRNG"
	"gitlab.com/xx_network/crypto/chacha"
	"gitlab.com/xx_network/crypto/csprng"
	xxMnemonic "gitlab.com/xx_network/crypto/mnemonic"
	"gitlab.com/xx_network/primitives/utils"
	"path/filepath"
	"strings"
)

const mnemonicFile = ".recovery"

// StoreSecretWithMnemonic creates a mnemonic and uses it to encrypt the secret.
// This encrypted data saved in storage.
func StoreSecretWithMnemonic(secret []byte, path string) (string, error) {
	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG).GetStream()

	// Ensure path is appended by filepath separator "/"
	if !strings.HasSuffix(path, string(filepath.Separator)) {
		path = path + string(filepath.Separator)
	}

	// Create a mnemonic
	mnemonic, err := xxMnemonic.GenerateMnemonic(rng, 32)
	if err != nil {
		return "", errors.Errorf("Failed to generate mnemonic: %v", err)
	}

	// Decode mnemonic
	decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
	if err != nil {
		return "", errors.Errorf("Failed to decode mnemonic: %v", err)
	}

	// Encrypt secret with mnemonic as key
	ciphertext, err := chacha.Encrypt(decodedMnemonic, secret, rng)
	if err != nil {
		return "", errors.Errorf("Failed to encrypt secret with mnemonic: %v", err)
	}

	// Save encrypted secret to file
	recoveryFile := path + mnemonicFile
	err = utils.WriteFileDef(recoveryFile, ciphertext)
	if err != nil {
		return "", errors.Errorf("Failed to save mnemonic information to file")
	}

	return mnemonic, nil
}

// LoadSecretWithMnemonic loads the encrypted secret from storage and decrypts
// the secret using the given mnemonic.
func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
	// Ensure path is appended by filepath separator "/"
	if !strings.HasSuffix(path, string(filepath.Separator)) {
		path = path + string(filepath.Separator)
	}

	// Ensure that the recovery file exists
	recoveryFile := path + mnemonicFile
	if !utils.Exists(recoveryFile) {
		return nil, errors.Errorf("Recovery file does not exist. " +
			"Did you properly set up recovery or provide an incorrect filepath?")
	}

	// Read file from storage
	data, err := utils.ReadFile(recoveryFile)
	if err != nil {
		return nil, errors.Errorf("Failed to load mnemonic information: %v", err)
	}

	// Decode mnemonic
	decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
	if err != nil {
		return nil, errors.Errorf("Failed to decode mnemonic: %v", err)
	}

	// Decrypt the stored secret
	secret, err = chacha.Decrypt(decodedMnemonic, data)
	if err != nil {
		return nil, errors.Errorf("Failed to decrypt secret: %v", err)
	}

	return secret, nil
}