package security

import (
	"strings"
	"testing"
)

func TestSecurityValidator(t *testing.T) {
	validator := NewSecurityValidator()
	
	// Test IP validation
	t.Run("IP Validation", func(t *testing.T) {
		validIPs := []string{"192.168.1.1", "127.0.0.1", "::1", "2001:db8::1"}
		for _, ip := range validIPs {
			if err := validator.ValidateIP(ip); err != nil {
				t.Errorf("Valid IP %s rejected: %v", ip, err)
			}
		}
		
		invalidIPs := []string{
			"", // empty
			"999.999.999.999", // invalid range
			"192.168.1.1; rm -rf /", // command injection
			"192.168.1.1`whoami`", // command substitution
			"192.168.1.1$(id)", // command substitution
			"192.168.1.1\ncat /etc/passwd", // newline injection
		}
		for _, ip := range invalidIPs {
			if err := validator.ValidateIP(ip); err == nil {
				t.Errorf("Invalid IP %s was accepted", ip)
			}
		}
	})
	
	// Test username validation
	t.Run("Username Validation", func(t *testing.T) {
		validUsernames := []string{"ubuntu", "user123", "_system", "test-user"}
		for _, username := range validUsernames {
			if err := validator.ValidateUsername(username); err != nil {
				t.Errorf("Valid username %s rejected: %v", username, err)
			}
		}
		
		invalidUsernames := []string{
			"", // empty
			"user; rm -rf /", // command injection
			"user`id`", // command substitution  
			"user$(whoami)", // command substitution
			"user\ncat /etc/passwd", // newline injection
			"user name", // space
			"user'test", // single quote
			"user\"test", // double quote
			"123user", // starts with number
		}
		for _, username := range invalidUsernames {
			if err := validator.ValidateUsername(username); err == nil {
				t.Errorf("Invalid username %s was accepted", username)
			}
		}
	})
	
	// Test password validation
	t.Run("Password Validation", func(t *testing.T) {
		validPasswords := []string{
			"", // empty is allowed
			"simplepassword",
			"complex@password#123",
			"unicode-пароль",
		}
		for _, password := range validPasswords {
			if err := validator.ValidatePassword(password); err != nil {
				t.Errorf("Valid password rejected: %v", err)
			}
		}
		
		invalidPasswords := []string{
			"password`whoami`", // command substitution
			"password$(id)", // command substitution  
			"password\necho malicious", // newline injection
			"password'break", // single quote injection
			"password$USER", // variable expansion
		}
		for _, password := range invalidPasswords {
			if err := validator.ValidatePassword(password); err == nil {
				t.Errorf("Invalid password was accepted")
			}
		}
	})
	
	// Test SSH key validation
	t.Run("SSH Key Validation", func(t *testing.T) {
		validKeys := []string{
			"", // empty is allowed
			"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----",
			"-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjE...\n-----END OPENSSH PRIVATE KEY-----",
		}
		for _, key := range validKeys {
			if err := validator.ValidateSSHKey(key); err != nil {
				t.Errorf("Valid SSH key rejected: %v", err)
			}
		}
		
		invalidKeys := []string{
			"ssh-rsa AAAAB3NzaC1yc2E...", // public key where private expected
			"invalid-key-format",
			"-----BEGIN RSA PRIVATE KEY-----\ntruncated", // malformed
		}
		for _, key := range invalidKeys {
			if err := validator.ValidateSSHKey(key); err == nil {
				t.Errorf("Invalid SSH key was accepted")
			}
		}
	})
	
	// Test command sanitization
	t.Run("Command Sanitization", func(t *testing.T) {
		testCases := []struct {
			input    string
			expected string
			safe     bool
		}{
			{"ls -la", "ls -la", true},
			{"systemctl status nginx", "systemctl status nginx", true},
			{"echo `whoami`", "echo whoami", false}, // backticks removed
			{"rm -rf /; echo done", "rm -rf / echo done", false}, // semicolon removed
			{"ls | grep test", "ls  grep test", false}, // pipe removed
			{"echo $USER", "echo USER", false}, // dollar removed
		}
		
		for _, tc := range testCases {
			result := validator.SanitizeForCommand(tc.input)
			if result != tc.expected {
				t.Errorf("Command sanitization failed: input=%s, expected=%s, got=%s", 
					tc.input, tc.expected, result)
			}
			
			isSafe := (result == tc.input)
			if isSafe != tc.safe {
				t.Errorf("Safety expectation failed for input=%s: expected safe=%v, got safe=%v",
					tc.input, tc.safe, isSafe)
			}
		}
	})
	
	// Test port validation
	t.Run("Port Validation", func(t *testing.T) {
		validPorts := []int{22, 80, 443, 8080, 3000}
		for _, port := range validPorts {
			if err := validator.ValidatePort(port); err != nil {
				t.Errorf("Valid port %d rejected: %v", port, err)
			}
		}
		
		invalidPorts := []int{0, -1, 65536, 99999}
		for _, port := range invalidPorts {
			if err := validator.ValidatePort(port); err == nil {
				t.Errorf("Invalid port %d was accepted", port)
			}
		}
	})
	
	// Test cluster secret validation
	t.Run("Cluster Secret Validation", func(t *testing.T) {
		validSecrets := []string{
			"abcdef1234567890abcdef1234567890", // 32 char hex
			"a1b2c3d4e5f6789012345678901234567890abcd", // longer hex
			"alphanumericSecr3t123456789012345678", // alphanumeric, 38 chars
		}
		for _, secret := range validSecrets {
			if err := validator.ValidateClusterSecret(secret); err != nil {
				t.Errorf("Valid secret rejected: %v", err)
			}
		}
		
		invalidSecrets := []string{
			"", // empty
			"short", // too short
			strings.Repeat("a", 200), // too long
		}
		for _, secret := range invalidSecrets {
			if err := validator.ValidateClusterSecret(secret); err == nil {
				t.Errorf("Invalid secret was accepted")
			}
		}
	})
}

func TestValidateSSHConnectionRequest(t *testing.T) {
	validator := NewSecurityValidator()
	
	// Test valid request
	err := validator.ValidateSSHConnectionRequest("192.168.1.1", "ubuntu", "password123", "", 22)
	if err != nil {
		t.Errorf("Valid SSH connection request rejected: %v", err)
	}
	
	// Test with SSH key instead of password
	err = validator.ValidateSSHConnectionRequest("192.168.1.1", "ubuntu", "", 
		"-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----", 22)
	if err != nil {
		t.Errorf("Valid SSH key request rejected: %v", err)
	}
	
	// Test missing both password and key
	err = validator.ValidateSSHConnectionRequest("192.168.1.1", "ubuntu", "", "", 22)
	if err == nil {
		t.Error("Request with no auth method was accepted")
	}
	
	// Test command injection in IP
	err = validator.ValidateSSHConnectionRequest("192.168.1.1; rm -rf /", "ubuntu", "password", "", 22)
	if err == nil {
		t.Error("Command injection in IP was accepted")
	}
	
	// Test command injection in username  
	err = validator.ValidateSSHConnectionRequest("192.168.1.1", "ubuntu`whoami`", "password", "", 22)
	if err == nil {
		t.Error("Command injection in username was accepted")
	}
}