package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"log"

	"github.com/jamesruan/sodium"
	"golang.org/x/crypto/argon2"
)

// deriveArgonKey generates a 32-bit cryptographic key using the Argon2id algorithm.
// Parameters:
//   - password: The plaintext password to be hashed.
//   - salt: The salt as a base64 encoded string.
//   - memLimit: The memory limit in bytes.
//   - opsLimit: The number of iterations.
//
// Returns:
//   - A byte slice representing the derived key.
//   - An error object, which is nil if no error occurs.
func deriveArgonKey(password, salt string, memLimit, opsLimit int) ([]byte, error) {
	if memLimit < 1024 || opsLimit < 1 {
		return nil, fmt.Errorf("invalid memory or operation limits")
	}

	// Decode salt from base64
	saltBytes, err := base64.StdEncoding.DecodeString(salt)
	if err != nil {
		return nil, fmt.Errorf("invalid salt: %v", err)
	}

	// Generate key using Argon2id
	// Note: We're assuming a fixed key length of 32 bytes and changing the threads
	key := argon2.IDKey([]byte(password), saltBytes, uint32(opsLimit), uint32(memLimit/1024), 1, 32)

	return key, nil
}

// decryptChaCha20poly1305 decrypts the given data using the ChaCha20-Poly1305 algorithm.
// Parameters:
//   - data: The encrypted data as a byte slice.
//   - key: The key for decryption as a byte slice.
//   - nonce: The nonce for decryption as a byte slice.
//
// Returns:
//   - A byte slice representing the decrypted data.
//   - An error object, which is nil if no error occurs.
func decryptChaCha20poly1305(data []byte, key []byte, nonce []byte) ([]byte, error) {
	reader := bytes.NewReader(data)
	header := sodium.SecretStreamXCPHeader{Bytes: nonce}
	decoder, err := sodium.MakeSecretStreamXCPDecoder(
		sodium.SecretStreamXCPKey{Bytes: key},
		reader,
		header)
	if err != nil {
		log.Println("Failed to make secret stream decoder", err)
		return nil, err
	}
	// Buffer to store the decrypted data
	decryptedData := make([]byte, len(data))
	n, err := decoder.Read(decryptedData)
	if err != nil && err != io.EOF {
		log.Println("Failed to read from decoder", err)
		return nil, err
	}
	return decryptedData[:n], nil
}
