// Package e2ee provides end-to-end encryption operations for the MapleFile SDK.
package e2ee

import (
	"encoding/json"
	"fmt"
)

// FileMetadata represents decrypted file metadata.
type FileMetadata struct {
	Name      string `json:"name"`
	MimeType  string `json:"mime_type"`
	Size      int64  `json:"size"`
	CreatedAt int64  `json:"created_at"`
}

// EncryptFile encrypts file content using the file key.
// Returns the combined nonce + ciphertext.
// NOTE: This uses ChaCha20-Poly1305 (12-byte nonce). For web frontend compatibility,
// use EncryptFileSecretBox instead.
func EncryptFile(plaintext, fileKey []byte) ([]byte, error) {
	encryptedData, err := Encrypt(plaintext, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to encrypt file: %w", err)
	}

	// Combine nonce and ciphertext for storage
	combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
	return combined, nil
}

// EncryptFileSecretBox encrypts file content using XSalsa20-Poly1305 (NaCl secretbox).
// Returns the combined nonce (24 bytes) + ciphertext.
// This is compatible with the web frontend's libsodium implementation.
func EncryptFileSecretBox(plaintext, fileKey []byte) ([]byte, error) {
	encryptedData, err := EncryptWithSecretBox(plaintext, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to encrypt file: %w", err)
	}

	// Combine nonce and ciphertext for storage (matching web frontend format)
	combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)
	return combined, nil
}

// DecryptFile decrypts file content using the file key.
// The input should be combined nonce + ciphertext.
// Auto-detects the cipher based on nonce size:
// - 24-byte nonce: XSalsa20-Poly1305 (web frontend / SecretBox)
// - 12-byte nonce: ChaCha20-Poly1305 (legacy native app)
func DecryptFile(encryptedData, fileKey []byte) ([]byte, error) {
	// Split nonce and ciphertext (auto-detect nonce size)
	nonce, ciphertext, err := SplitNonceAndCiphertextAuto(encryptedData)
	if err != nil {
		return nil, fmt.Errorf("failed to split encrypted data: %w", err)
	}

	// Decrypt using appropriate algorithm based on nonce size
	plaintext, err := DecryptWithAlgorithm(ciphertext, nonce, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt file: %w", err)
	}

	return plaintext, nil
}

// EncryptFileWithNonce encrypts file content and returns the ciphertext and nonce separately.
func EncryptFileWithNonce(plaintext, fileKey []byte) (ciphertext []byte, nonce []byte, err error) {
	encryptedData, err := Encrypt(plaintext, fileKey)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to encrypt file: %w", err)
	}

	return encryptedData.Ciphertext, encryptedData.Nonce, nil
}

// DecryptFileWithNonce decrypts file content using separate ciphertext and nonce.
func DecryptFileWithNonce(ciphertext, nonce, fileKey []byte) ([]byte, error) {
	plaintext, err := Decrypt(ciphertext, nonce, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt file: %w", err)
	}

	return plaintext, nil
}

// EncryptMetadata encrypts file metadata using the file key.
// Returns base64-encoded combined nonce + ciphertext.
// NOTE: This uses ChaCha20-Poly1305 (12-byte nonce). For web frontend compatibility,
// use EncryptMetadataSecretBox instead.
func EncryptMetadata(metadata *FileMetadata, fileKey []byte) (string, error) {
	// Convert metadata to JSON
	metadataBytes, err := json.Marshal(metadata)
	if err != nil {
		return "", fmt.Errorf("failed to marshal metadata: %w", err)
	}

	// Encrypt metadata
	encryptedData, err := Encrypt(metadataBytes, fileKey)
	if err != nil {
		return "", fmt.Errorf("failed to encrypt metadata: %w", err)
	}

	// Combine nonce and ciphertext
	combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)

	// Encode to base64
	return EncodeToBase64(combined), nil
}

// EncryptMetadataSecretBox encrypts file metadata using XSalsa20-Poly1305 (NaCl secretbox).
// Returns base64-encoded combined nonce + ciphertext.
// This is compatible with the web frontend's libsodium implementation.
func EncryptMetadataSecretBox(metadata *FileMetadata, fileKey []byte) (string, error) {
	// Convert metadata to JSON
	metadataBytes, err := json.Marshal(metadata)
	if err != nil {
		return "", fmt.Errorf("failed to marshal metadata: %w", err)
	}

	// Encrypt metadata using SecretBox
	encryptedData, err := EncryptWithSecretBox(metadataBytes, fileKey)
	if err != nil {
		return "", fmt.Errorf("failed to encrypt metadata: %w", err)
	}

	// Combine nonce and ciphertext
	combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)

	// Encode to base64
	return EncodeToBase64(combined), nil
}

// DecryptMetadata decrypts file metadata using the file key.
// The input should be base64-encoded combined nonce + ciphertext.
func DecryptMetadata(encryptedMetadata string, fileKey []byte) (*FileMetadata, error) {
	// Decode from base64
	combined, err := DecodeFromBase64(encryptedMetadata)
	if err != nil {
		return nil, fmt.Errorf("failed to decode encrypted metadata: %w", err)
	}

	// Split nonce and ciphertext
	nonce, ciphertext, err := SplitNonceAndCiphertext(combined)
	if err != nil {
		return nil, fmt.Errorf("failed to split encrypted metadata: %w", err)
	}

	// Decrypt
	decryptedBytes, err := Decrypt(ciphertext, nonce, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt metadata: %w", err)
	}

	// Parse JSON
	var metadata FileMetadata
	if err := json.Unmarshal(decryptedBytes, &metadata); err != nil {
		return nil, fmt.Errorf("failed to parse decrypted metadata: %w", err)
	}

	return &metadata, nil
}

// EncryptMetadataWithNonce encrypts file metadata and returns nonce separately.
func EncryptMetadataWithNonce(metadata *FileMetadata, fileKey []byte) (ciphertext []byte, nonce []byte, err error) {
	// Convert metadata to JSON
	metadataBytes, err := json.Marshal(metadata)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal metadata: %w", err)
	}

	// Encrypt metadata
	encryptedData, err := Encrypt(metadataBytes, fileKey)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to encrypt metadata: %w", err)
	}

	return encryptedData.Ciphertext, encryptedData.Nonce, nil
}

// DecryptMetadataWithNonce decrypts file metadata using separate ciphertext and nonce.
func DecryptMetadataWithNonce(ciphertext, nonce, fileKey []byte) (*FileMetadata, error) {
	// Decrypt
	decryptedBytes, err := Decrypt(ciphertext, nonce, fileKey)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt metadata: %w", err)
	}

	// Parse JSON
	var metadata FileMetadata
	if err := json.Unmarshal(decryptedBytes, &metadata); err != nil {
		return nil, fmt.Errorf("failed to parse decrypted metadata: %w", err)
	}

	return &metadata, nil
}

// EncryptData encrypts arbitrary data using the provided key.
// Returns base64-encoded combined nonce + ciphertext.
func EncryptData(data, key []byte) (string, error) {
	encryptedData, err := Encrypt(data, key)
	if err != nil {
		return "", fmt.Errorf("failed to encrypt data: %w", err)
	}

	// Combine nonce and ciphertext
	combined := CombineNonceAndCiphertext(encryptedData.Nonce, encryptedData.Ciphertext)

	// Encode to base64
	return EncodeToBase64(combined), nil
}

// DecryptData decrypts arbitrary data using the provided key.
// The input should be base64-encoded combined nonce + ciphertext.
func DecryptData(encryptedData string, key []byte) ([]byte, error) {
	// Decode from base64
	combined, err := DecodeFromBase64(encryptedData)
	if err != nil {
		return nil, fmt.Errorf("failed to decode encrypted data: %w", err)
	}

	// Split nonce and ciphertext
	nonce, ciphertext, err := SplitNonceAndCiphertext(combined)
	if err != nil {
		return nil, fmt.Errorf("failed to split encrypted data: %w", err)
	}

	// Decrypt
	plaintext, err := Decrypt(ciphertext, nonce, key)
	if err != nil {
		return nil, fmt.Errorf("failed to decrypt data: %w", err)
	}

	return plaintext, nil
}
