package security

import (
	"fmt"
	"os"
	"os/exec"
	"syscall"

	"git.gostacks.org/iwasforcedtobehere/WiseTLP/autotlp/pkg/utils"
)

// PrivilegeManager handles privilege escalation and security operations
type PrivilegeManager struct {
	logger *utils.Logger
}

// NewPrivilegeManager creates a new privilege manager
func NewPrivilegeManager(logger *utils.Logger) *PrivilegeManager {
	return &PrivilegeManager{
		logger: logger.WithComponent("security"),
	}
}

// RequireRoot ensures the current operation has root privileges
func (pm *PrivilegeManager) RequireRoot(operation string) error {
	if os.Geteuid() == 0 {
		return nil
	}

	pm.logger.Info("Root privileges required", "operation", operation)
	return fmt.Errorf("operation '%s' requires root privileges", operation)
}

// EscalateIfNeeded escalates privileges if not already running as root
func (pm *PrivilegeManager) EscalateIfNeeded(args []string, operation string) error {
	if os.Geteuid() == 0 {
		return nil
	}

	pm.logger.Info("Escalating privileges", "operation", operation)

	// Check if sudo is available
	if !utils.CommandExists("sudo") {
		return fmt.Errorf("sudo is required for privilege escalation but not available")
	}

	// Inform user about privilege escalation
	fmt.Printf("\nPrivilege escalation required for: %s\n", operation)
	fmt.Println("This operation needs administrative privileges to:")

	switch operation {
	case "install_tlp":
		fmt.Println("- Install TLP packages using system package manager")
		fmt.Println("- Enable and start TLP systemd service")
		fmt.Println("- Mask conflicting power management services")
	case "apply_config":
		fmt.Println("- Write TLP configuration to system directories")
		fmt.Println("- Reload TLP service with new configuration")
	case "system_info":
		fmt.Println("- Access hardware information from system files")
		fmt.Println("- Read power management settings")
	default:
		fmt.Printf("- Perform system operation: %s\n", operation)
	}

	if !utils.GetUserConfirmation("Continue with privilege escalation?") {
		return fmt.Errorf("privilege escalation declined by user")
	}

	// Execute with sudo
	return pm.executeSudo(args)
}

// executeSudo executes the current program with sudo
func (pm *PrivilegeManager) executeSudo(args []string) error {
	// Get current executable path
	executable, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %w", err)
	}

	// Prepare sudo command
	sudoArgs := append([]string{executable}, args...)
	cmd := exec.Command("sudo", sudoArgs...)

	// Preserve environment variables that might be needed
	cmd.Env = os.Environ()

	// Connect stdio
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	pm.logger.Info("Executing with sudo", "command", cmd.String())

	// Execute the command
	err = cmd.Run()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			// Get exit code from the elevated process
			if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
				os.Exit(status.ExitStatus())
			}
		}
		return fmt.Errorf("sudo execution failed: %w", err)
	}

	// If we reach here, the elevated process completed successfully
	os.Exit(0)
	return nil // This line will never be reached
}

// CheckSudoAccess verifies that the user can use sudo
func (pm *PrivilegeManager) CheckSudoAccess() error {
	if os.Geteuid() == 0 {
		return nil // Already root
	}

	if !utils.CommandExists("sudo") {
		return fmt.Errorf("sudo command not available")
	}

	// Test sudo access with a harmless command
	cmd := exec.Command("sudo", "-n", "true")
	if err := cmd.Run(); err != nil {
		// -n flag failed, user needs to authenticate
		pm.logger.Debug("Sudo authentication required")
		return nil // This is expected for most users
	}

	pm.logger.Debug("Sudo access confirmed without authentication")
	return nil
}

// DropPrivileges drops root privileges if running as root
func (pm *PrivilegeManager) DropPrivileges() error {
	if os.Geteuid() != 0 {
		return nil // Not running as root
	}

	// Get the original user info from environment
	sudoUID := os.Getenv("SUDO_UID")
	sudoGID := os.Getenv("SUDO_GID")

	if sudoUID == "" || sudoGID == "" {
		pm.logger.Warn("Cannot drop privileges: SUDO_UID/SUDO_GID not set")
		return nil // Don't fail, just log warning
	}

	pm.logger.Info("Dropping root privileges", "uid", sudoUID, "gid", sudoGID)

	// This is a placeholder - actual privilege dropping requires careful handling
	// and is typically done at the start of the program, not mid-execution
	pm.logger.Debug("Privilege dropping not implemented for mid-execution")

	return nil
}

// ValidateSystemAccess validates that the program has necessary system access
func (pm *PrivilegeManager) ValidateSystemAccess() error {
	// Check read access to system information
	systemPaths := []string{
		"/proc/cpuinfo",
		"/proc/meminfo",
		"/sys/class/power_supply",
		"/sys/devices/system/cpu",
	}

	for _, path := range systemPaths {
		if utils.FileExists(path) {
			// Try to read the file/directory
			if file, err := os.Open(path); err != nil {
				pm.logger.Warn("Limited access to system path", "path", path, "error", err)
			} else {
				file.Close()
			}
		}
	}

	return nil
}

// SecureFilePermissions sets secure permissions on configuration files
func (pm *PrivilegeManager) SecureFilePermissions(filePath string) error {
	// Set file permissions to be readable only by owner and group
	if err := os.Chmod(filePath, 0640); err != nil {
		return fmt.Errorf("failed to set secure permissions on %s: %w", filePath, err)
	}

	pm.logger.Debug("Set secure file permissions", "file", filePath, "mode", "0640")
	return nil
}

// ValidateInput performs basic input validation for security
func ValidateInput(input string, maxLength int, allowedChars string) error {
	if len(input) > maxLength {
		return fmt.Errorf("input too long: %d characters (max %d)", len(input), maxLength)
	}

	if len(input) == 0 {
		return fmt.Errorf("input cannot be empty")
	}

	// Basic validation against null bytes and control characters
	for i, r := range input {
		if r == 0 {
			return fmt.Errorf("null byte at position %d", i)
		}
		if r < 32 && r != 9 && r != 10 && r != 13 { // Allow tab, LF, CR
			return fmt.Errorf("control character at position %d", i)
		}
	}

	return nil
}

// SanitizeFilePath sanitizes file paths to prevent directory traversal
func SanitizeFilePath(path string) (string, error) {
	// Basic path validation
	if path == "" {
		return "", fmt.Errorf("empty path")
	}

	// Check for directory traversal attempts
	if len(path) > 1 && (path[0] == '/' || path[1] == ':') {
		// Absolute path - this might be intentional, so we allow it
		// but log it for security awareness
	}

	// Check for dangerous patterns
	dangerousPatterns := []string{
		"../",
		"..\\",
		"./",
		".\\",
	}

	for _, pattern := range dangerousPatterns {
		if len(path) >= len(pattern) {
			for i := 0; i <= len(path)-len(pattern); i++ {
				if path[i:i+len(pattern)] == pattern {
					return "", fmt.Errorf("potentially dangerous path pattern: %s", pattern)
				}
			}
		}
	}

	return path, nil
}
