package nginx

import (
	"path/filepath"
	"regexp"
	"testing"
)

// Mock nginx -T output for testing purposes
const mockNginxTOutput = `
# configuration file /etc/nginx/nginx.conf:
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
error_log  /var/log/nginx/error.local.log notice;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    access_log  /var/log/nginx/access.local.log  main;

    sendfile        on;
    keepalive_timeout  65;
    gzip  on;

    server {
        listen       80;
        server_name  localhost;
        
        access_log   /var/log/nginx/server.access.log;
        error_log    /var/log/nginx/server.error.log warn;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
    }
}

stream {
    error_log /var/log/nginx/stream.error.log info;
    
    server {
        listen 3306;
        proxy_pass backend;
    }
}
`

// Mock nginx -T output with relative paths
const mockNginxTOutputRelative = `
# configuration file /etc/nginx/nginx.conf:
user  nginx;
worker_processes  auto;

error_log  logs/error.log notice;
pid        /var/run/nginx.pid;

http {
    access_log  logs/access.log  main;
    
    server {
        listen       80;
        server_name  localhost;
        
        access_log   logs/server.access.log;
        error_log    logs/server.error.log warn;
    }
}
`

// Mock nginx -T output with access_log off
const mockNginxTOutputOff = `
# configuration file /etc/nginx/nginx.conf:
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;

http {
    access_log  off;
    
    server {
        listen       80;
        server_name  localhost;
        
        access_log   /var/log/nginx/server.access.log;
        error_log    /var/log/nginx/server.error.log warn;
    }
}
`

// Mock nginx -T output with commented log directives
const mockNginxTOutputCommented = `
# configuration file /etc/nginx/nginx.conf:
user  nginx;
worker_processes  auto;

# error_log  /var/log/nginx/commented.error.log notice;
error_log  /var/log/nginx/error.log notice;

http {
    # access_log  /var/log/nginx/commented.access.log  main;
    access_log  /var/log/nginx/access.log  main;
    
    server {
        listen       80;
        server_name  localhost;
        
        # access_log   /var/log/nginx/commented.server.access.log;
        access_log   /var/log/nginx/server.access.log;
        # error_log    /var/log/nginx/commented.server.error.log warn;
        error_log    /var/log/nginx/server.error.log warn;
    }
}
`

func TestAccessLogRegexParsing(t *testing.T) {
	testCases := []struct {
		name          string
		nginxTOutput  string
		expectedPath  string
		shouldHaveLog bool
	}{
		{
			name:          "standard access log",
			nginxTOutput:  "access_log  /var/log/nginx/access.log  main;",
			expectedPath:  "/var/log/nginx/access.log",
			shouldHaveLog: true,
		},
		{
			name:          "access log turned off",
			nginxTOutput:  "access_log  off;",
			expectedPath:  "",
			shouldHaveLog: false,
		},
		{
			name:          "no access log directive",
			nginxTOutput:  "server_name  localhost;",
			expectedPath:  "",
			shouldHaveLog: false,
		},
		{
			name:          "indented access log",
			nginxTOutput:  "    access_log  /var/log/nginx/server.log;",
			expectedPath:  "/var/log/nginx/server.log",
			shouldHaveLog: true,
		},
		{
			name:          "multiple access logs - should get first",
			nginxTOutput:  "access_log  /var/log/nginx/access1.log  main;\naccess_log  /var/log/nginx/access2.log  combined;",
			expectedPath:  "/var/log/nginx/access1.log",
			shouldHaveLog: true,
		},
		{
			name:          "commented access log should be ignored",
			nginxTOutput:  "# access_log  /var/log/nginx/commented.access.log  main;\naccess_log  /var/log/nginx/access.log  main;",
			expectedPath:  "/var/log/nginx/access.log",
			shouldHaveLog: true,
		},
		{
			name:          "only commented access log",
			nginxTOutput:  "# access_log  /var/log/nginx/commented.access.log  main;",
			expectedPath:  "",
			shouldHaveLog: false,
		},
	}

	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			matches := accessLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)

			if !tc.shouldHaveLog {
				if len(matches) > 0 {
					// Check if it's the "off" directive
					if len(matches[0]) >= 2 {
						logPath := matches[0][1]
						if logPath != "off" {
							t.Errorf("Expected no valid access log, but found: %s", logPath)
						}
					}
				}
				return
			}

			if len(matches) == 0 {
				t.Errorf("Expected to find access log directive, but found none")
				return
			}

			if len(matches[0]) < 2 {
				t.Errorf("Expected regex match to have at least 2 groups, got %d", len(matches[0]))
				return
			}

			logPath := matches[0][1]

			if logPath != tc.expectedPath {
				t.Errorf("Expected access log path %s, got %s", tc.expectedPath, logPath)
			}
		})
	}
}

func TestErrorLogRegexParsing(t *testing.T) {
	testCases := []struct {
		name          string
		nginxTOutput  string
		expectedPath  string
		shouldHaveLog bool
	}{
		{
			name:          "standard error log",
			nginxTOutput:  "error_log  /var/log/nginx/error.log notice;",
			expectedPath:  "/var/log/nginx/error.log",
			shouldHaveLog: true,
		},
		{
			name:          "error log without level",
			nginxTOutput:  "error_log  /var/log/nginx/error.log;",
			expectedPath:  "/var/log/nginx/error.log",
			shouldHaveLog: true,
		},
		{
			name:          "no error log directive",
			nginxTOutput:  "server_name  localhost;",
			expectedPath:  "",
			shouldHaveLog: false,
		},
		{
			name:          "indented error log",
			nginxTOutput:  "        error_log  /var/log/nginx/server.error.log warn;",
			expectedPath:  "/var/log/nginx/server.error.log",
			shouldHaveLog: true,
		},
		{
			name:          "multiple error logs - should get first",
			nginxTOutput:  "error_log  /var/log/nginx/error1.log  notice;\nerror_log  /var/log/nginx/error2.log  warn;",
			expectedPath:  "/var/log/nginx/error1.log",
			shouldHaveLog: true,
		},
		{
			name:          "commented error log should be ignored",
			nginxTOutput:  "# error_log  /var/log/nginx/commented.error.log  notice;\nerror_log  /var/log/nginx/error.log  notice;",
			expectedPath:  "/var/log/nginx/error.log",
			shouldHaveLog: true,
		},
		{
			name:          "only commented error log",
			nginxTOutput:  "# error_log  /var/log/nginx/commented.error.log  notice;",
			expectedPath:  "",
			shouldHaveLog: false,
		},
	}

	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			matches := errorLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)

			if !tc.shouldHaveLog {
				if len(matches) > 0 {
					t.Errorf("Expected no error log directive, but found: %v", matches)
				}
				return
			}

			if len(matches) == 0 {
				t.Errorf("Expected to find error log directive, but found none")
				return
			}

			if len(matches[0]) < 2 {
				t.Errorf("Expected regex match to have at least 2 groups, got %d", len(matches[0]))
				return
			}

			logPath := matches[0][1]

			if logPath != tc.expectedPath {
				t.Errorf("Expected error log path %s, got %s", tc.expectedPath, logPath)
			}
		})
	}
}

func TestLogPathParsing(t *testing.T) {
	testCases := []struct {
		name               string
		nginxTOutput       string
		expectedAccessPath string
		expectedErrorPath  string
		shouldHaveAccess   bool
		shouldHaveError    bool
	}{
		{
			name:               "complete configuration",
			nginxTOutput:       mockNginxTOutput,
			expectedAccessPath: "/var/log/nginx/access.log",
			expectedErrorPath:  "/var/log/nginx/error.log",
			shouldHaveAccess:   true,
			shouldHaveError:    true,
		},
		{
			name:               "configuration with commented directives",
			nginxTOutput:       mockNginxTOutputCommented,
			expectedAccessPath: "/var/log/nginx/access.log",
			expectedErrorPath:  "/var/log/nginx/error.log",
			shouldHaveAccess:   true,
			shouldHaveError:    true,
		},
		{
			name:               "access log turned off",
			nginxTOutput:       mockNginxTOutputOff,
			expectedAccessPath: "/var/log/nginx/server.access.log", // Should get the server-level access log
			expectedErrorPath:  "/var/log/nginx/error.log",
			shouldHaveAccess:   true,
			shouldHaveError:    true,
		},
		{
			name:               "empty configuration",
			nginxTOutput:       "",
			expectedAccessPath: "",
			expectedErrorPath:  "",
			shouldHaveAccess:   false,
			shouldHaveError:    false,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Test access log parsing
			accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)
			accessMatches := accessLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)

			var foundAccessPath string
			for _, match := range accessMatches {
				if len(match) >= 2 {
					logPath := match[1]
					if logPath != "off" {
						foundAccessPath = logPath
						break
					}
				}
			}

			if tc.shouldHaveAccess {
				if foundAccessPath == "" {
					t.Errorf("Expected access log path %s, but found none", tc.expectedAccessPath)
				} else if foundAccessPath != tc.expectedAccessPath {
					t.Errorf("Expected access log path %s, got %s", tc.expectedAccessPath, foundAccessPath)
				}
			} else {
				if foundAccessPath != "" {
					t.Errorf("Expected no access log path, but found %s", foundAccessPath)
				}
			}

			// Test error log parsing
			errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)
			errorMatches := errorLogRegex.FindAllStringSubmatch(tc.nginxTOutput, -1)

			var foundErrorPath string
			if len(errorMatches) > 0 && len(errorMatches[0]) >= 2 {
				foundErrorPath = errorMatches[0][1]
			}

			if tc.shouldHaveError {
				if foundErrorPath == "" {
					t.Errorf("Expected error log path %s, but found none", tc.expectedErrorPath)
				} else if foundErrorPath != tc.expectedErrorPath {
					t.Errorf("Expected error log path %s, got %s", tc.expectedErrorPath, foundErrorPath)
				}
			} else {
				if foundErrorPath != "" {
					t.Errorf("Expected no error log path, but found %s", foundErrorPath)
				}
			}
		})
	}
}

func TestRelativePathHandling(t *testing.T) {
	// Mock GetPrefix function for testing
	originalGetPrefix := GetPrefix
	defer func() {
		// Restore original function (if needed for other tests)
		_ = originalGetPrefix
	}()

	testPrefix := "/usr/local/nginx"

	testCases := []struct {
		name         string
		inputPath    string
		expectedPath string
		isRelative   bool
	}{
		{
			name:         "absolute path",
			inputPath:    "/var/log/nginx/access.log",
			expectedPath: "/var/log/nginx/access.log",
			isRelative:   false,
		},
		{
			name:         "relative path",
			inputPath:    "logs/access.log",
			expectedPath: filepath.Join(testPrefix, "logs/access.log"),
			isRelative:   true,
		},
		{
			name:         "relative path with ./",
			inputPath:    "./logs/access.log",
			expectedPath: filepath.Join(testPrefix, "./logs/access.log"),
			isRelative:   true,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var result string

			if tc.isRelative {
				result = filepath.Join(testPrefix, tc.inputPath)
			} else {
				result = tc.inputPath
			}

			if result != tc.expectedPath {
				t.Errorf("Expected path %s, got %s", tc.expectedPath, result)
			}
		})
	}
}

func TestComplexNginxConfiguration(t *testing.T) {
	complexConfig := `
# Main configuration
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    
    # Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    
    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
        root /var/www/html;
        index index.html index.htm index.nginx-debian.html;
        
        access_log /var/log/nginx/default.access.log;
        error_log /var/log/nginx/default.error.log;
        
        location / {
            try_files $uri $uri/ =404;
        }
        
        location ~ /\.ht {
            deny all;
        }
    }
    
    server {
        listen 443 ssl http2;
        server_name example.com;
        root /var/www/example.com;
        
        access_log /var/log/nginx/example.access.log combined;
        error_log /var/log/nginx/example.error.log info;
        
        ssl_certificate /etc/ssl/certs/example.com.pem;
        ssl_certificate_key /etc/ssl/private/example.com.key;
    }
}

stream {
    error_log /var/log/nginx/stream.error.log info;
    
    upstream backend {
        server 192.168.1.100:3306;
        server 192.168.1.101:3306;
    }
    
    server {
        listen 3306;
        proxy_pass backend;
        proxy_timeout 1s;
        proxy_responses 1;
    }
}
`

	// Test that we can extract the main access log and error log from complex config
	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)
	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)

	// Find all access logs
	accessMatches := accessLogRegex.FindAllStringSubmatch(complexConfig, -1)
	if len(accessMatches) == 0 {
		t.Error("Expected to find access log directives in complex config")
	} else {
		firstAccessLog := accessMatches[0][1]
		expectedFirstAccess := "/var/log/nginx/access.log"
		if firstAccessLog != expectedFirstAccess {
			t.Errorf("Expected first access log to be %s, got %s", expectedFirstAccess, firstAccessLog)
		}
		t.Logf("Found %d access log directives, first: %s", len(accessMatches), firstAccessLog)
	}

	// Find all error logs
	errorMatches := errorLogRegex.FindAllStringSubmatch(complexConfig, -1)
	if len(errorMatches) == 0 {
		t.Error("Expected to find error log directives in complex config")
	} else {
		firstErrorLog := errorMatches[0][1]
		expectedFirstError := "/var/log/nginx/error.log"
		if firstErrorLog != expectedFirstError {
			t.Errorf("Expected first error log to be %s, got %s", expectedFirstError, firstErrorLog)
		}
		t.Logf("Found %d error log directives, first: %s", len(errorMatches), firstErrorLog)
	}
}

func TestCommentedDirectivesIgnored(t *testing.T) {
	testConfig := `
# Main configuration
user nginx;
worker_processes auto;

# These should be ignored
# error_log  /var/log/nginx/commented.error.log notice;
# access_log  /var/log/nginx/commented.access.log  main;

# Real directives
error_log /var/log/nginx/error.log warn;

http {
    # This should be ignored too
    # access_log /var/log/nginx/commented.http.access.log combined;
    
    # Real directive
    access_log /var/log/nginx/access.log main;
    
    server {
        listen 80;
        server_name example.com;
        
        # Commented server-level logs should be ignored
        # access_log /var/log/nginx/commented.server.access.log;
        # error_log /var/log/nginx/commented.server.error.log warn;
        
        # Real server-level logs
        access_log /var/log/nginx/server.access.log;
        error_log /var/log/nginx/server.error.log info;
    }
}
`

	// Test access log parsing ignores comments
	accessLogRegex := regexp.MustCompile(AccessLogRegexPattern)
	accessMatches := accessLogRegex.FindAllStringSubmatch(testConfig, -1)

	expectedAccessLogs := []string{
		"/var/log/nginx/access.log",
		"/var/log/nginx/server.access.log",
	}

	if len(accessMatches) != len(expectedAccessLogs) {
		t.Errorf("Expected %d access log matches, got %d", len(expectedAccessLogs), len(accessMatches))
	}

	for i, match := range accessMatches {
		if i < len(expectedAccessLogs) {
			if match[1] != expectedAccessLogs[i] {
				t.Errorf("Expected access log %d to be %s, got %s", i, expectedAccessLogs[i], match[1])
			}
		}
	}

	// Test error log parsing ignores comments
	errorLogRegex := regexp.MustCompile(ErrorLogRegexPattern)
	errorMatches := errorLogRegex.FindAllStringSubmatch(testConfig, -1)

	expectedErrorLogs := []string{
		"/var/log/nginx/error.log",
		"/var/log/nginx/server.error.log",
	}

	if len(errorMatches) != len(expectedErrorLogs) {
		t.Errorf("Expected %d error log matches, got %d", len(expectedErrorLogs), len(errorMatches))
	}

	for i, match := range errorMatches {
		if i < len(expectedErrorLogs) {
			if match[1] != expectedErrorLogs[i] {
				t.Errorf("Expected error log %d to be %s, got %s", i, expectedErrorLogs[i], match[1])
			}
		}
	}
}
