// internal/config/userdata.go
package config

import (
	"crypto/sha256"
	"encoding/hex"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// GetUserDataDir returns the appropriate directory for storing application data
// following platform-specific conventions:
// - Windows: %LOCALAPPDATA%\{appName}
// - macOS: ~/Library/Application Support/{appName}
// - Linux: ~/.local/share/{appName} (or $XDG_DATA_HOME/{appName})
func GetUserDataDir(appName string) (string, error) {
	var baseDir string
	var err error

	switch runtime.GOOS {
	case "windows":
		// Use LOCALAPPDATA for application data on Windows
		baseDir = os.Getenv("LOCALAPPDATA")
		if baseDir == "" {
			// Fallback to APPDATA if LOCALAPPDATA is not set
			baseDir = os.Getenv("APPDATA")
			if baseDir == "" {
				// Last resort: use UserConfigDir
				baseDir, err = os.UserConfigDir()
				if err != nil {
					return "", err
				}
			}
		}

	case "darwin":
		// Use ~/Library/Application Support on macOS
		home, err := os.UserHomeDir()
		if err != nil {
			return "", err
		}
		baseDir = filepath.Join(home, "Library", "Application Support")

	default:
		// Linux and other Unix-like systems
		// Follow XDG Base Directory Specification
		if xdgData := os.Getenv("XDG_DATA_HOME"); xdgData != "" {
			baseDir = xdgData
		} else {
			home, err := os.UserHomeDir()
			if err != nil {
				return "", err
			}
			baseDir = filepath.Join(home, ".local", "share")
		}
	}

	// Combine with app name
	appDataDir := filepath.Join(baseDir, appName)

	// Create the directory if it doesn't exist with restrictive permissions
	if err := os.MkdirAll(appDataDir, 0700); err != nil {
		return "", err
	}

	return appDataDir, nil
}

// GetUserSpecificDataDir returns the data directory for a specific user.
// User data is isolated by hashing the email to create a unique directory name.
// This ensures:
// 1. Different users have completely separate storage
// 2. Email addresses are not exposed in directory names
// 3. The same user always gets the same directory
//
// Directory structure:
//
//	{appDataDir}/users/{emailHash}/
//	  ├── local_files/    # File and collection metadata (LevelDB)
//	  ├── sync_state/     # Sync state (LevelDB)
//	  ├── cache/          # Application cache (LevelDB)
//	  └── files/          # Downloaded decrypted files
//	      └── {collectionId}/
//	          └── {filename}
func GetUserSpecificDataDir(appName, userEmail string) (string, error) {
	if userEmail == "" {
		return "", nil // No user logged in, return empty
	}

	appDataDir, err := GetUserDataDir(appName)
	if err != nil {
		return "", err
	}

	// Hash the email to create a privacy-preserving directory name
	emailHash := hashEmail(userEmail)

	// Create user-specific directory
	userDir := filepath.Join(appDataDir, "users", emailHash)

	// Create the directory with restrictive permissions (owner only)
	if err := os.MkdirAll(userDir, 0700); err != nil {
		return "", err
	}

	return userDir, nil
}

// GetUserFilesDir returns the directory where decrypted files are stored for a user.
// Files are organized by collection: {userDir}/files/{collectionId}/{filename}
func GetUserFilesDir(appName, userEmail string) (string, error) {
	userDir, err := GetUserSpecificDataDir(appName, userEmail)
	if err != nil {
		return "", err
	}
	if userDir == "" {
		return "", nil // No user logged in
	}

	filesDir := filepath.Join(userDir, "files")

	// Create with restrictive permissions
	if err := os.MkdirAll(filesDir, 0700); err != nil {
		return "", err
	}

	return filesDir, nil
}

// hashEmail creates a SHA256 hash of the email address (lowercase, trimmed).
// Returns a shortened hash (first 16 characters) for more readable directory names
// while still maintaining uniqueness.
func hashEmail(email string) string {
	// Normalize email: lowercase and trim whitespace
	normalizedEmail := strings.ToLower(strings.TrimSpace(email))

	// Create SHA256 hash
	hash := sha256.Sum256([]byte(normalizedEmail))

	// Return first 16 characters of hex representation (64 bits of entropy is sufficient)
	return hex.EncodeToString(hash[:])[:16]
}

// GetEmailHashForPath returns the hash that would be used for a user's directory.
// This can be used to check if a user's data exists without revealing the email.
func GetEmailHashForPath(userEmail string) string {
	if userEmail == "" {
		return ""
	}
	return hashEmail(userEmail)
}

// GetUserSearchIndexDir returns the directory where the Bleve search index is stored.
// Returns: {userDir}/search/index.bleve
func GetUserSearchIndexDir(appName, userEmail string) (string, error) {
	userDir, err := GetUserSpecificDataDir(appName, userEmail)
	if err != nil {
		return "", err
	}
	if userDir == "" {
		return "", nil // No user logged in
	}

	searchIndexPath := filepath.Join(userDir, "search", "index.bleve")

	// Create parent directory with restrictive permissions
	searchDir := filepath.Join(userDir, "search")
	if err := os.MkdirAll(searchDir, 0700); err != nil {
		return "", err
	}

	return searchIndexPath, nil
}
