package config

import (
	"os"
	"strconv"
	"time"
)

// Config holds all configuration for the application
type Config struct {
	Server    ServerConfig
	Executor  ExecutorConfig
	Languages map[string]LanguageConfig
	Sandbox   SandboxConfig
}

// ServerConfig holds server-related configurations
type ServerConfig struct {
	Port         string
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
	IdleTimeout  time.Duration
}

// ExecutorConfig holds executor-related configurations
type ExecutorConfig struct {
	ConcurrentExecutions int
	QueueCapacity        int
	DefaultTimeout       time.Duration
}

// LanguageConfig holds language-specific configurations
type LanguageConfig struct {
	Name        string
	Image       string
	MemoryLimit string
	CPULimit    string
	TimeoutSec  int
	CompileCmd  []string
	RunCmd      []string
	FileExt     string
	VersionCmd  []string
}

// SandboxConfig holds sandbox-related configurations
type SandboxConfig struct {
	NetworkDisabled bool
	MemorySwapLimit string
	PidsLimit       int64
}

// GetConfig returns the application configuration
func GetConfig() *Config {
	return &Config{
		Server: ServerConfig{
			Port:         getEnv("PORT", "8080"),
			ReadTimeout:  time.Duration(getEnvAsInt("READ_TIMEOUT", 15)) * time.Second,
			WriteTimeout: time.Duration(getEnvAsInt("WRITE_TIMEOUT", 15)) * time.Second,
			IdleTimeout:  time.Duration(getEnvAsInt("IDLE_TIMEOUT", 90)) * time.Second,
		},
		Executor: ExecutorConfig{
			ConcurrentExecutions: getEnvAsInt("CONCURRENT_EXECUTIONS", 100),
			QueueCapacity:        getEnvAsInt("QUEUE_CAPACITY", 1000),
			DefaultTimeout:       time.Duration(getEnvAsInt("DEFAULT_TIMEOUT", 30)) * time.Second,
		},
		Languages: getLanguageConfigs(),
		Sandbox: SandboxConfig{
			NetworkDisabled: getEnvAsBool("SANDBOX_NETWORK_DISABLED", true),
			MemorySwapLimit: getEnv("SANDBOX_MEMORY_SWAP_LIMIT", "0"),
			PidsLimit:       int64(getEnvAsInt("SANDBOX_PIDS_LIMIT", 50)),
		},
	}
}

// getLanguageConfigs returns configurations for all supported languages
func getLanguageConfigs() map[string]LanguageConfig {
	return map[string]LanguageConfig{
		"python": {
			Name:        "Python",
			Image:       "python:3.9-slim",
			MemoryLimit: "100m",
			CPULimit:    "0.1",
			TimeoutSec:  90,
			RunCmd:      []string{"python", "-c"},
			FileExt:     ".py",
			VersionCmd:  []string{"python", "--version"},
		},
		"java": {
			Name:        "Java",
			Image:       "eclipse-temurin:11-jdk",
			MemoryLimit: "400m",
			CPULimit:    "0.5",
			TimeoutSec:  100,
			CompileCmd:  []string{"javac"},
			RunCmd:      []string{"java"},
			FileExt:     ".java",
			VersionCmd:  []string{"java", "-version"},
		},
		"c": {
			Name:        "C",
			Image:       "gcc:latest",
			MemoryLimit: "100m",
			CPULimit:    "0.1",
			TimeoutSec:  90,
			CompileCmd:  []string{"gcc", "-o", "program"},
			RunCmd:      []string{"./program"},
			FileExt:     ".c",
			VersionCmd:  []string{"gcc", "--version"},
		},
		"cpp": {
			Name:        "C++",
			Image:       "gcc:latest",
			MemoryLimit: "100m",
			CPULimit:    "0.1",
			TimeoutSec:  90,
			CompileCmd:  []string{"g++", "-o", "program"},
			RunCmd:      []string{"./program"},
			FileExt:     ".cpp",
			VersionCmd:  []string{"g++", "--version"},
		},
		"javascript": {
			Name:        "JavaScript",
			Image:       "node:16-alpine",
			MemoryLimit: "100m",
			CPULimit:    "0.1",
			TimeoutSec:  90,
			RunCmd:      []string{"node", "-e"},
			FileExt:     ".js",
			VersionCmd:  []string{"node", "--version"},
		},
		"golang": {
			Name:        "Go",
			Image:       "golang:1.19-alpine",
			MemoryLimit: "100m",
			CPULimit:    "0.1",
			TimeoutSec:  90,
			CompileCmd:  []string{"go", "build", "-o", "program"},
			RunCmd:      []string{"./program"},
			FileExt:     ".go",
			VersionCmd:  []string{"go", "version"},
		},
	}
}

// Helper functions to get environment variables with defaults
func getEnv(key, defaultValue string) string {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	return value
}

func getEnvAsInt(key string, defaultValue int) int {
	valueStr := getEnv(key, "")
	if value, err := strconv.Atoi(valueStr); err == nil {
		return value
	}
	return defaultValue
}

func getEnvAsBool(key string, defaultValue bool) bool {
	valueStr := getEnv(key, "")
	if value, err := strconv.ParseBool(valueStr); err == nil {
		return value
	}
	return defaultValue
}
