package security

import (
	"os"
	"path/filepath"
	"testing"

	"github.com/jfraeys/fetch_ml/internal/fileutil"
)

func TestSecurePathValidator_ValidatePath(t *testing.T) {
	// Create a temporary directory for testing
	tempDir := t.TempDir()
	validator := fileutil.NewSecurePathValidator(tempDir)

	tests := []struct {
		name      string
		input     string
		wantErr   bool
		errMsg    string
	}{
		{
			name:    "valid relative path",
			input:   "subdir/file.txt",
			wantErr: false,
		},
		{
			name:    "valid absolute path within base",
			input:   filepath.Join(tempDir, "file.txt"),
			wantErr: false,
		},
		{
			name:    "path traversal attempt with dots",
			input:   "../etc/passwd",
			wantErr: true,
			errMsg:  "path escapes base directory",
		},
		{
			name:    "path traversal attempt with encoded dots",
			input:   "...//...//etc/passwd",
			wantErr: true,
			errMsg:  "path escapes base directory",
		},
		{
			name:    "absolute path outside base",
			input:   "/etc/passwd",
			wantErr: true,
			errMsg:  "path escapes base directory",
		},
		{
			name:    "empty path returns base",
			input:   "",
			wantErr: false,
		},
		{
			name:    "single dot current directory",
			input:   ".",
			wantErr: false,
		},
	}

	// Create subdir for tests that need it
	_ = os.MkdirAll(filepath.Join(tempDir, "subdir"), 0755)

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := validator.ValidatePath(tt.input)
			if tt.wantErr {
				if err == nil {
					t.Errorf("ValidatePath() error = nil, wantErr %v", tt.wantErr)
					return
				}
				if tt.errMsg != "" && err.Error()[:len(tt.errMsg)] != tt.errMsg {
					t.Errorf("ValidatePath() error = %v, want %v", err, tt.errMsg)
				}
			} else {
				if err != nil {
					t.Errorf("ValidatePath() unexpected error = %v", err)
					return
				}
				if got == "" {
					t.Errorf("ValidatePath() returned empty path")
				}
			}
		})
	}
}

func TestSecurePathValidator_SymlinkEscape(t *testing.T) {
	// Create temp directories
	tempDir := t.TempDir()
	outsideDir := t.TempDir()
	validator := fileutil.NewSecurePathValidator(tempDir)

	// Create a file outside the base directory
	outsideFile := filepath.Join(outsideDir, "secret.txt")
	if err := os.WriteFile(outsideFile, []byte("secret"), 0600); err != nil {
		t.Fatalf("Failed to create outside file: %v", err)
	}

	// Create a symlink inside tempDir pointing outside
	symlinkPath := filepath.Join(tempDir, "link")
	if err := os.Symlink(outsideFile, symlinkPath); err != nil {
		t.Fatalf("Failed to create symlink: %v", err)
	}

	// Attempt to access through symlink should fail
	_, err := validator.ValidatePath("link")
	if err == nil {
		t.Errorf("Symlink escape should be blocked: %v", err)
	}
}

func TestSecurePathValidator_BasePathNotSet(t *testing.T) {
	validator := fileutil.NewSecurePathValidator("")
	_, err := validator.ValidatePath("test.txt")
	if err == nil || err.Error() != "base path not set" {
		t.Errorf("Expected 'base path not set' error, got: %v", err)
	}
}
