package config

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

// ErrUnknownProfile is returned when an unrecognized environment profile is encountered
var ErrUnknownProfile = errors.New("unknown environment profile")

// EnvironmentProfile represents the deployment environment
type EnvironmentProfile int

// Environment profiles for configuration defaults
const (
	ProfileLocal EnvironmentProfile = iota
	ProfileContainer
	ProfileCI
	ProfileProduction
)

// DetectEnvironment determines the current environment profile
func DetectEnvironment() EnvironmentProfile {
	// CI detection
	if os.Getenv("CI") != "" || os.Getenv("GITHUB_ACTIONS") != "" || os.Getenv("GITLAB_CI") != "" {
		return ProfileCI
	}

	// Container detection
	if _, err := os.Stat("/.dockerenv"); err == nil {
		return ProfileContainer
	}
	if os.Getenv("KUBERNETES_SERVICE_HOST") != "" {
		return ProfileContainer
	}
	if os.Getenv("CONTAINER") != "" {
		return ProfileContainer
	}

	// Production detection (customizable)
	if os.Getenv("FETCH_ML_ENV") == "prod" || os.Getenv("ENV") == "prod" {
		return ProfileProduction
	}

	// Default to local development
	return ProfileLocal
}

// SmartDefaults provides environment-aware default values
type SmartDefaults struct {
	Profile EnvironmentProfile
}

// GetSmartDefaults returns defaults for the current environment
func GetSmartDefaults() *SmartDefaults {
	return &SmartDefaults{
		Profile: DetectEnvironment(),
	}
}

// Host returns the appropriate default host
func (s *SmartDefaults) Host() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "host.docker.internal", nil // Docker Desktop/Colima
	case ProfileProduction:
		return "0.0.0.0", nil
	case ProfileLocal:
		return "localhost", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// BasePath returns the appropriate default base path
func (s *SmartDefaults) BasePath() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "/workspace/ml-experiments", nil
	case ProfileProduction:
		return "/var/lib/fetch_ml/experiments", nil
	case ProfileLocal:
		if home, err := os.UserHomeDir(); err == nil {
			return filepath.Join(home, "ml-experiments"), nil
		}
		return "./ml-experiments", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// DataDir returns the appropriate default data directory
func (s *SmartDefaults) DataDir() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "/workspace/data", nil
	case ProfileProduction:
		return "/var/lib/fetch_ml/data", nil
	case ProfileLocal:
		if home, err := os.UserHomeDir(); err == nil {
			return filepath.Join(home, "ml-data"), nil
		}
		return "./data", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// RedisAddr returns the appropriate default Redis address
func (s *SmartDefaults) RedisAddr() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "redis:6379", nil // Service name in containers
	case ProfileProduction:
		return "redis:6379", nil
	case ProfileLocal:
		return "localhost:6379", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// SSHKeyPath returns the appropriate default SSH key path
func (s *SmartDefaults) SSHKeyPath() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "/workspace/.ssh/id_rsa", nil
	case ProfileProduction:
		return "/etc/fetch_ml/ssh/id_rsa", nil
	case ProfileLocal:
		if home, err := os.UserHomeDir(); err == nil {
			return filepath.Join(home, ".ssh", "id_rsa"), nil
		}
		return "~/.ssh/id_rsa", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// KnownHostsPath returns the appropriate default known_hosts path
func (s *SmartDefaults) KnownHostsPath() (string, error) {
	switch s.Profile {
	case ProfileContainer, ProfileCI:
		return "/workspace/.ssh/known_hosts", nil
	case ProfileProduction:
		return "/etc/fetch_ml/ssh/known_hosts", nil
	case ProfileLocal:
		if home, err := os.UserHomeDir(); err == nil {
			return filepath.Join(home, ".ssh", "known_hosts"), nil
		}
		return "~/.ssh/known_hosts", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// LogLevel returns the appropriate default log level
func (s *SmartDefaults) LogLevel() (string, error) {
	switch s.Profile {
	case ProfileCI:
		return "debug", nil // More verbose for CI debugging
	case ProfileProduction:
		return "info", nil
	case ProfileLocal, ProfileContainer:
		return "info", nil
	default:
		return "", fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// MaxWorkers returns the appropriate default worker count
func (s *SmartDefaults) MaxWorkers() (int, error) {
	switch s.Profile {
	case ProfileCI:
		return 1, nil // Conservative for CI
	case ProfileProduction:
		return runtime.NumCPU(), nil // Scale with CPU cores
	case ProfileLocal, ProfileContainer:
		return 2, nil // Reasonable default for local dev
	default:
		return 0, fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// PollInterval returns the appropriate default poll interval in seconds
func (s *SmartDefaults) PollInterval() (int, error) {
	switch s.Profile {
	case ProfileCI:
		return 1, nil // Fast polling for quick tests
	case ProfileProduction:
		return 10, nil // Conservative for production
	case ProfileLocal, ProfileContainer:
		return 5, nil // Balanced default
	default:
		return 0, fmt.Errorf("%w: %v", ErrUnknownProfile, s.Profile)
	}
}

// IsInContainer returns true if running in a container environment
func (s *SmartDefaults) IsInContainer() bool {
	return s.Profile == ProfileContainer || s.Profile == ProfileCI
}

// IsProduction returns true if this is a production environment
func (s *SmartDefaults) IsProduction() bool {
	return s.Profile == ProfileProduction
}

// IsCI returns true if this is a CI environment
func (s *SmartDefaults) IsCI() bool {
	return s.Profile == ProfileCI
}

// ExpandPath expands ~ and environment variables in paths
func (s *SmartDefaults) ExpandPath(path string) string {
	if strings.HasPrefix(path, "~/") {
		if home, err := os.UserHomeDir(); err == nil {
			path = filepath.Join(home, path[2:])
		}
	}

	// Expand environment variables
	path = os.ExpandEnv(path)

	return path
}

// ModeBasedPath returns the appropriate path for a given mode
func ModeBasedPath(mode string, subpath string) string {
	switch mode {
	case "dev":
		return filepath.Join("./data/dev", subpath)
	case "prod":
		return filepath.Join("./data/prod", subpath)
	case "ci":
		return filepath.Join("./data/ci", subpath)
	case "prod-smoke":
		return filepath.Join("./data/prod-smoke", subpath)
	default:
		return filepath.Join("./data/dev", subpath)
	}
}

// ModeBasedBasePath returns the experiments base path for a given mode
func ModeBasedBasePath(mode string) string {
	return ModeBasedPath(mode, "experiments")
}

// ModeBasedDataDir returns the data directory for a given mode
func ModeBasedDataDir(mode string) string {
	return ModeBasedPath(mode, "active")
}

// ModeBasedLogDir returns the logs directory for a given mode
func ModeBasedLogDir(mode string) string {
	return ModeBasedPath(mode, "logs")
}

// GetEnvironmentDescription returns a human-readable description
func (s *SmartDefaults) GetEnvironmentDescription() string {
	switch s.Profile {
	case ProfileLocal:
		return "Local Development"
	case ProfileContainer:
		return "Container Environment"
	case ProfileCI:
		return "CI/CD Environment"
	case ProfileProduction:
		return "Production Environment"
	default:
		return "Unknown Environment"
	}
}
