// Package config provides a unified API for managing application configuration
// Location: monorepo/native/desktop/maplefile/internal/config/methods.go
package config

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"strings"
	"time"
)

// Implementation of ConfigService methods

// getConfig is an internal method to get the current configuration
func (s *configService) getConfig(ctx context.Context) (*Config, error) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.repo.LoadConfig(ctx)
}

// saveConfig is an internal method to save the configuration
func (s *configService) saveConfig(ctx context.Context, config *Config) error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.repo.SaveConfig(ctx, config)
}

// GetConfig returns the complete configuration
func (s *configService) GetConfig(ctx context.Context) (*Config, error) {
	return s.getConfig(ctx)
}

// GetAppDataDirPath returns the proper application data directory path
// The directory is mode-aware: "maplefile-dev" for dev mode, "maplefile" for production.
func (s *configService) GetAppDataDirPath(ctx context.Context) (string, error) {
	return GetUserDataDir(GetAppName())
}

// GetUserDataDirPath returns the data directory path for a specific user.
// This path is:
// 1. Isolated per user (different users get different directories)
// 2. Isolated per environment (dev vs production)
// 3. Privacy-preserving (email is hashed to create directory name)
//
// Structure: {appDataDir}/users/{emailHash}/
func (s *configService) GetUserDataDirPath(ctx context.Context, userEmail string) (string, error) {
	if userEmail == "" {
		return "", fmt.Errorf("user email is required")
	}
	return GetUserSpecificDataDir(GetAppName(), userEmail)
}

// GetUserFilesDirPath returns the directory where decrypted files are stored for a user.
// Files are organized by collection: {userDir}/files/{collectionId}/{filename}
func (s *configService) GetUserFilesDirPath(ctx context.Context, userEmail string) (string, error) {
	if userEmail == "" {
		return "", fmt.Errorf("user email is required")
	}
	return GetUserFilesDir(GetAppName(), userEmail)
}

// GetUserSearchIndexDir returns the search index directory path for a specific user.
func (s *configService) GetUserSearchIndexDir(ctx context.Context, userEmail string) (string, error) {
	if userEmail == "" {
		return "", fmt.Errorf("user email is required")
	}
	return GetUserSearchIndexDir(GetAppName(), userEmail)
}

// GetLoggedInUserEmail returns the email of the currently logged-in user.
// Returns an empty string if no user is logged in.
func (s *configService) GetLoggedInUserEmail(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	if config.Credentials == nil {
		return "", nil
	}
	return config.Credentials.Email, nil
}

// GetCloudProviderAddress returns the cloud provider address
func (s *configService) GetCloudProviderAddress(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	return config.CloudProviderAddress, nil
}

// SetCloudProviderAddress updates the cloud provider address with security validation.
// In production mode, the address cannot be changed.
// In dev mode, HTTP is allowed for localhost only.
func (s *configService) SetCloudProviderAddress(ctx context.Context, address string) error {
	mode := os.Getenv("MAPLEFILE_MODE")
	if mode == "" {
		mode = "dev"
	}

	// Security: Block address changes in production mode
	if mode == "production" {
		return fmt.Errorf("cloud provider address cannot be changed in production mode")
	}

	// Validate URL format
	if err := validateCloudProviderURL(address, mode); err != nil {
		return fmt.Errorf("invalid cloud provider address: %w", err)
	}

	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.CloudProviderAddress = address
	return s.saveConfig(ctx, config)
}

// validateCloudProviderURL validates the cloud provider URL based on the current mode.
// Returns an error if the URL is invalid or doesn't meet security requirements.
func validateCloudProviderURL(rawURL string, mode string) error {
	if rawURL == "" {
		return fmt.Errorf("URL cannot be empty")
	}

	parsedURL, err := url.Parse(rawURL)
	if err != nil {
		return fmt.Errorf("malformed URL: %w", err)
	}

	// Validate scheme
	scheme := strings.ToLower(parsedURL.Scheme)
	if scheme != "http" && scheme != "https" {
		return fmt.Errorf("URL scheme must be http or https, got: %s", scheme)
	}

	// Validate host is present
	if parsedURL.Host == "" {
		return fmt.Errorf("URL must have a host")
	}

	// Security: In dev mode, allow HTTP only for localhost
	if mode == "dev" && scheme == "http" {
		host := strings.ToLower(parsedURL.Hostname())
		if host != "localhost" && host != "127.0.0.1" && !strings.HasPrefix(host, "192.168.") && !strings.HasPrefix(host, "10.") {
			return fmt.Errorf("HTTP is only allowed for localhost/local network in dev mode; use HTTPS for remote servers")
		}
	}

	// Reject URLs with credentials embedded
	if parsedURL.User != nil {
		return fmt.Errorf("URL must not contain embedded credentials")
	}

	return nil
}

// SetLoggedInUserCredentials updates the authenticated user's credentials
func (s *configService) SetLoggedInUserCredentials(
	ctx context.Context,
	email string,
	accessToken string,
	accessTokenExpiryTime *time.Time,
	refreshToken string,
	refreshTokenExpiryTime *time.Time,
) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.Credentials = &Credentials{
		Email:                  email,
		AccessToken:            accessToken,
		AccessTokenExpiryTime:  accessTokenExpiryTime,
		RefreshToken:           refreshToken,
		RefreshTokenExpiryTime: refreshTokenExpiryTime,
	}
	return s.saveConfig(ctx, config)
}

// GetLoggedInUserCredentials returns the authenticated user's credentials
func (s *configService) GetLoggedInUserCredentials(ctx context.Context) (*Credentials, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return nil, err
	}
	return config.Credentials, nil
}

// ClearLoggedInUserCredentials clears the authenticated user's credentials
func (s *configService) ClearLoggedInUserCredentials(ctx context.Context) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	// Clear credentials by setting them to empty values
	config.Credentials = &Credentials{
		Email:                  "",
		AccessToken:            "",
		AccessTokenExpiryTime:  nil,
		RefreshToken:           "",
		RefreshTokenExpiryTime: nil,
	}

	return s.saveConfig(ctx, config)
}

// Desktop-specific methods

// GetWindowSize returns the configured window size
func (s *configService) GetWindowSize(ctx context.Context) (width int, height int, err error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return 0, 0, err
	}
	return config.WindowWidth, config.WindowHeight, nil
}

// SetWindowSize updates the window size configuration
func (s *configService) SetWindowSize(ctx context.Context, width int, height int) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.WindowWidth = width
	config.WindowHeight = height
	return s.saveConfig(ctx, config)
}

// GetTheme returns the configured theme
func (s *configService) GetTheme(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	return config.Theme, nil
}

// SetTheme updates the theme configuration
func (s *configService) SetTheme(ctx context.Context, theme string) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.Theme = theme
	return s.saveConfig(ctx, config)
}

// GetLanguage returns the configured language
func (s *configService) GetLanguage(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	return config.Language, nil
}

// SetLanguage updates the language configuration
func (s *configService) SetLanguage(ctx context.Context, language string) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.Language = language
	return s.saveConfig(ctx, config)
}

// GetSyncMode returns the configured sync mode
func (s *configService) GetSyncMode(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	return config.SyncMode, nil
}

// SetSyncMode updates the sync mode configuration
func (s *configService) SetSyncMode(ctx context.Context, mode string) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.SyncMode = mode
	return s.saveConfig(ctx, config)
}

// GetAutoSync returns whether automatic sync is enabled
func (s *configService) GetAutoSync(ctx context.Context) (bool, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return false, err
	}
	return config.AutoSync, nil
}

// SetAutoSync updates the automatic sync setting
func (s *configService) SetAutoSync(ctx context.Context, enabled bool) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.AutoSync = enabled
	return s.saveConfig(ctx, config)
}

// GetSyncInterval returns the sync interval in minutes
func (s *configService) GetSyncInterval(ctx context.Context) (int, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return 0, err
	}
	return config.SyncIntervalMinutes, nil
}

// SetSyncInterval updates the sync interval configuration
func (s *configService) SetSyncInterval(ctx context.Context, minutes int) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.SyncIntervalMinutes = minutes
	return s.saveConfig(ctx, config)
}

// GetShowHiddenFiles returns whether hidden files should be shown
func (s *configService) GetShowHiddenFiles(ctx context.Context) (bool, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return false, err
	}
	return config.ShowHiddenFiles, nil
}

// SetShowHiddenFiles updates the show hidden files setting
func (s *configService) SetShowHiddenFiles(ctx context.Context, show bool) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.ShowHiddenFiles = show
	return s.saveConfig(ctx, config)
}

// GetDefaultView returns the configured default view
func (s *configService) GetDefaultView(ctx context.Context) (string, error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", err
	}
	return config.DefaultView, nil
}

// SetDefaultView updates the default view configuration
func (s *configService) SetDefaultView(ctx context.Context, view string) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.DefaultView = view
	return s.saveConfig(ctx, config)
}

// GetSortPreferences returns the configured sort preferences
func (s *configService) GetSortPreferences(ctx context.Context) (sortBy string, sortOrder string, err error) {
	config, err := s.getConfig(ctx)
	if err != nil {
		return "", "", err
	}
	return config.SortBy, config.SortOrder, nil
}

// SetSortPreferences updates the sort preferences
func (s *configService) SetSortPreferences(ctx context.Context, sortBy string, sortOrder string) error {
	config, err := s.getConfig(ctx)
	if err != nil {
		return err
	}

	config.SortBy = sortBy
	config.SortOrder = sortOrder
	return s.saveConfig(ctx, config)
}

// Ensure our implementation satisfies the interface
var _ ConfigService = (*configService)(nil)
