package app

import (
	"encoding/base64"
	"fmt"

	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/maplefile/e2ee"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/utils"
)

// VerifyPassword verifies a password against stored encrypted data
func (a *Application) VerifyPassword(password string) (bool, error) {
	// Get current session with encrypted data
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return false, fmt.Errorf("no active session")
	}

	// Check if we have the encrypted data needed for verification
	if session.Salt == "" || session.EncryptedMasterKey == "" {
		return false, fmt.Errorf("session missing encrypted data for password verification")
	}

	// Decode base64 inputs
	salt, err := base64.StdEncoding.DecodeString(session.Salt)
	if err != nil {
		a.logger.Error("Failed to decode salt", zap.Error(err))
		return false, fmt.Errorf("invalid salt encoding")
	}

	encryptedMasterKeyBytes, err := base64.StdEncoding.DecodeString(session.EncryptedMasterKey)
	if err != nil {
		a.logger.Error("Failed to decode encrypted master key", zap.Error(err))
		return false, fmt.Errorf("invalid master key encoding")
	}

	// Determine which KDF algorithm to use
	kdfAlgorithm := session.KDFAlgorithm
	if kdfAlgorithm == "" {
		kdfAlgorithm = e2ee.PBKDF2Algorithm
	}

	// Try to derive KEK and decrypt master key using SecureKeyChain
	// If decryption succeeds, password is correct
	keychain, err := e2ee.NewSecureKeyChainWithAlgorithm(password, salt, kdfAlgorithm)
	if err != nil {
		a.logger.Debug("Password verification failed - could not derive key", zap.String("email", utils.MaskEmail(session.Email)))
		return false, nil // Password is incorrect, but not an error condition
	}
	defer keychain.Clear()

	// Split nonce and ciphertext from encrypted master key
	// Use auto-detection to handle both ChaCha20 (12-byte nonce) and XSalsa20 (24-byte nonce)
	masterKeyNonce, masterKeyCiphertext, err := e2ee.SplitNonceAndCiphertextAuto(encryptedMasterKeyBytes)
	if err != nil {
		a.logger.Error("Failed to split encrypted master key", zap.Error(err))
		return false, fmt.Errorf("invalid master key format")
	}

	encryptedMasterKeyStruct := &e2ee.EncryptedKey{
		Ciphertext: masterKeyCiphertext,
		Nonce:      masterKeyNonce,
	}

	// Try to decrypt the master key into protected memory
	masterKey, err := keychain.DecryptMasterKeySecure(encryptedMasterKeyStruct)
	if err != nil {
		a.logger.Debug("Password verification failed - incorrect password", zap.String("email", utils.MaskEmail(session.Email)))
		return false, nil // Password is incorrect, but not an error condition
	}

	// Copy master key bytes before destroying the buffer
	// We'll cache it after verification succeeds
	masterKeyBytes := make([]byte, masterKey.Size())
	copy(masterKeyBytes, masterKey.Bytes())
	masterKey.Destroy()

	// Cache the master key for the session (already decrypted, no need to re-derive)
	if err := a.keyCache.StoreMasterKey(session.Email, masterKeyBytes); err != nil {
		a.logger.Warn("Failed to cache master key during password verification", zap.Error(err))
		// Don't fail verification if caching fails
	} else {
		a.logger.Info("Master key cached successfully during password verification", zap.String("email", utils.MaskEmail(session.Email)))
	}

	a.logger.Info("Password verified successfully", zap.String("email", utils.MaskEmail(session.Email)))
	return true, nil
}

// StorePasswordForSession stores password for current session (used by PasswordPrompt)
func (a *Application) StorePasswordForSession(password string) error {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return fmt.Errorf("no active session")
	}

	if err := a.passwordStore.StorePassword(session.Email, password); err != nil {
		a.logger.Error("Failed to store password for session", zap.String("email", utils.MaskEmail(session.Email)), zap.Error(err))
		return err
	}

	a.logger.Info("Password re-stored in secure RAM after app restart", zap.String("email", utils.MaskEmail(session.Email)))

	// Note: Master key caching is now handled in VerifyPassword()
	// to avoid running PBKDF2 twice. The password verification step
	// already derives KEK and decrypts the master key, so we cache it there.
	// This eliminates redundant key derivation delay.

	return nil
}

// GetStoredPassword retrieves the stored password for current session
func (a *Application) GetStoredPassword() (string, error) {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return "", fmt.Errorf("no active session")
	}

	return a.passwordStore.GetPassword(session.Email)
}

// HasStoredPassword checks if password is stored for current session
func (a *Application) HasStoredPassword() bool {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return false
	}

	return a.passwordStore.HasPassword(session.Email)
}

// ClearStoredPassword clears the stored password (optional, for security-sensitive operations)
func (a *Application) ClearStoredPassword() error {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return fmt.Errorf("no active session")
	}

	return a.passwordStore.ClearPassword(session.Email)
}

// cacheMasterKeyFromPassword decrypts and caches the master key for the session
// This is an internal helper method used by CompleteLogin and StorePasswordForSession
func (a *Application) cacheMasterKeyFromPassword(email, password, saltBase64, encryptedMasterKeyBase64, kdfAlgorithm string) error {
	// Default to PBKDF2-SHA256
	if kdfAlgorithm == "" {
		kdfAlgorithm = e2ee.PBKDF2Algorithm
	}

	// Decode base64 inputs
	salt, err := base64.StdEncoding.DecodeString(saltBase64)
	if err != nil {
		return fmt.Errorf("invalid salt encoding: %w", err)
	}

	encryptedMasterKeyBytes, err := base64.StdEncoding.DecodeString(encryptedMasterKeyBase64)
	if err != nil {
		return fmt.Errorf("invalid master key encoding: %w", err)
	}

	// Create secure keychain to derive KEK using the correct KDF algorithm
	keychain, err := e2ee.NewSecureKeyChainWithAlgorithm(password, salt, kdfAlgorithm)
	if err != nil {
		return fmt.Errorf("failed to derive KEK: %w", err)
	}
	defer keychain.Clear()

	// Split nonce and ciphertext using 24-byte nonce (XSalsa20 secretbox format from web frontend)
	masterKeyNonce, masterKeyCiphertext, err := e2ee.SplitNonceAndCiphertextSecretBox(encryptedMasterKeyBytes)
	if err != nil {
		return fmt.Errorf("invalid master key format: %w", err)
	}

	encryptedMasterKeyStruct := &e2ee.EncryptedKey{
		Ciphertext: masterKeyCiphertext,
		Nonce:      masterKeyNonce,
	}

	// Decrypt master key into secure buffer (auto-detects cipher based on nonce size)
	masterKey, err := keychain.DecryptMasterKeySecure(encryptedMasterKeyStruct)
	if err != nil {
		return fmt.Errorf("failed to decrypt master key: %w", err)
	}

	// CRITICAL: Copy bytes BEFORE destroying the buffer to avoid SIGBUS fault
	// masterKey.Bytes() returns a pointer to LockedBuffer memory which becomes
	// invalid after Destroy() is called
	masterKeyBytes := make([]byte, masterKey.Size())
	copy(masterKeyBytes, masterKey.Bytes())

	// Now safely destroy the secure buffer
	masterKey.Destroy()

	// Store the copied bytes in cache
	if err := a.keyCache.StoreMasterKey(email, masterKeyBytes); err != nil {
		return fmt.Errorf("failed to cache master key: %w", err)
	}

	a.logger.Info("Master key cached successfully for session", zap.String("email", utils.MaskEmail(email)))
	return nil
}

// GetCachedMasterKey retrieves the cached master key for the current session
// This is exported and can be called from frontend for file operations
// Returns the master key bytes and a cleanup function that MUST be called when done
func (a *Application) GetCachedMasterKey() ([]byte, func(), error) {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return nil, nil, fmt.Errorf("no active session")
	}

	return a.keyCache.GetMasterKey(session.Email)
}

// HasCachedMasterKey checks if a master key is cached for the current session
func (a *Application) HasCachedMasterKey() bool {
	session, err := a.authService.GetCurrentSession(a.ctx)
	if err != nil || session == nil {
		return false
	}

	return a.keyCache.HasMasterKey(session.Email)
}
