package config

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

	"github.com/jfraeys/fetch_ml/internal/config"
	"github.com/jfraeys/fetch_ml/internal/storage"
)

func TestExpandPath(t *testing.T) {
	t.Parallel() // Enable parallel execution

	// Test empty path
	result := config.ExpandPath("")
	if result != "" {
		t.Errorf("Expected empty string for empty input, got %s", result)
	}

	// Test normal path (no expansion)
	result = config.ExpandPath("/some/path")
	if result != "/some/path" {
		t.Errorf("Expected /some/path, got %s", result)
	}

	// Test environment variable expansion
	_ = os.Setenv("TEST_VAR", "test_value")
	defer func() { _ = os.Unsetenv("XDG_CONFIG_HOME") }()

	result = config.ExpandPath("/path/$TEST_VAR/file")
	expected := "/path/test_value/file"
	if result != expected {
		t.Errorf("Expected %s, got %s", expected, result)
	}

	// Test tilde expansion (if home directory is available)
	home, err := os.UserHomeDir()
	if err == nil {
		result = config.ExpandPath("~/test")
		expected := filepath.Join(home, "test")
		if result != expected {
			t.Errorf("Expected %s, got %s", expected, result)
		}
	}

	// Test combination of tilde and env vars
	if err == nil {
		// To ensure consistent tilde expansion for this test, temporarily set HOME
		tempHomeDir := t.TempDir()
		_ = os.Setenv("HOME", tempHomeDir)
		defer func() { _ = os.Unsetenv("HOME") }() // Clean up HOME env var

		_ = os.Setenv("TEST_DIR", "mydir")
		defer func() { _ = os.Unsetenv("TEST_DIR") }()

		result = config.ExpandPath("~/$TEST_DIR/file")
		// The expected path should use the temporarily set HOME
		expected := filepath.Join(tempHomeDir, "mydir", "file")
		if result != expected {
			t.Errorf("Expected %s, got %s", expected, result)
		}
	}
}

func TestResolveConfigPath(t *testing.T) {
	t.Parallel() // Enable parallel execution

	// Create a temporary directory for testing
	tempDir := t.TempDir()

	// Test with absolute path that exists
	configFile := filepath.Join(tempDir, "config.yaml")
	err := os.WriteFile(configFile, []byte("test: config"), 0600)
	if err != nil {
		t.Fatalf("Failed to create test config file: %v", err)
	}

	result, err := config.ResolveConfigPath(configFile)
	if err != nil {
		t.Errorf("Expected no error for existing absolute path, got %v", err)
	}
	if result != config.ExpandPath(configFile) {
		t.Errorf("Expected %s, got %s", config.ExpandPath(configFile), result)
	}

	// Test with relative path that doesn't exist
	_, err = config.ResolveConfigPath("nonexistent.yaml")
	if err == nil {
		t.Error("Expected error for non-existent config file")
	}

	// Test with relative path that exists in current directory
	relativeConfig := "relative_config.yaml"
	err = os.WriteFile(relativeConfig, []byte("test: config"), 0600)
	if err != nil {
		t.Fatalf("Failed to create relative config file: %v", err)
	}
	defer func() { _ = os.Remove(relativeConfig) }()

	result, err = config.ResolveConfigPath(relativeConfig)
	if err != nil {
		t.Errorf("Expected no error for existing relative path, got %v", err)
	}
	if result != config.ExpandPath(relativeConfig) {
		t.Errorf("Expected %s, got %s", config.ExpandPath(relativeConfig), result)
	}

	// Test with relative path that exists in configs subdirectory
	configsDir := filepath.Join(tempDir, "configs")
	err = os.MkdirAll(configsDir, 0750)
	if err != nil {
		t.Fatalf("Failed to create configs directory: %v", err)
	}

	configInConfigs := filepath.Join(configsDir, "config.yaml")
	err = os.WriteFile(configInConfigs, []byte("test: config"), 0600)
	if err != nil {
		t.Fatalf("Failed to create config in configs directory: %v", err)
	}

	// Change to temp directory to test relative path resolution
	originalWd, err := os.Getwd()
	if err != nil {
		t.Fatalf("Failed to get current working directory: %v", err)
	}
	defer func() { _ = os.Chdir(originalWd) }()

	err = os.Chdir(tempDir)
	if err != nil {
		t.Fatalf("Failed to change to temp directory: %v", err)
	}

	result, err = config.ResolveConfigPath("config.yaml")
	if err != nil {
		t.Errorf("Expected no error for config in configs subdirectory, got %v", err)
	}
	// The result should be the expanded path to the config file
	if !strings.Contains(result, "config.yaml") {
		t.Errorf("Expected result to contain config.yaml, got %s", result)
	}
}

func TestNewJobPaths(t *testing.T) {
	t.Parallel() // Enable parallel execution

	basePath := "/test/base"
	jobPaths := storage.NewJobPaths(basePath)

	if jobPaths.BasePath != basePath {
		t.Errorf("Expected BasePath %s, got %s", basePath, jobPaths.BasePath)
	}
}

func TestJobPathsMethods(t *testing.T) {
	t.Parallel() // Enable parallel execution

	basePath := "/test/base"
	jobPaths := storage.NewJobPaths(basePath)

	// Test all path methods
	tests := []struct {
		name     string
		method   func() string
		expected string
	}{
		{"PendingPath", jobPaths.PendingPath, "/test/base/pending"},
		{"RunningPath", jobPaths.RunningPath, "/test/base/running"},
		{"FinishedPath", jobPaths.FinishedPath, "/test/base/finished"},
		{"FailedPath", jobPaths.FailedPath, "/test/base/failed"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := tt.method()
			if result != tt.expected {
				t.Errorf("Expected %s, got %s", tt.expected, result)
			}
		})
	}
}

func TestJobPathsWithComplexBase(t *testing.T) {
	t.Parallel() // Enable parallel execution

	basePath := "/very/complex/base/path/with/subdirs"
	jobPaths := storage.NewJobPaths(basePath)

	expectedPending := filepath.Join(basePath, "pending")
	if jobPaths.PendingPath() != expectedPending {
		t.Errorf("Expected %s, got %s", expectedPending, jobPaths.PendingPath())
	}

	expectedRunning := filepath.Join(basePath, "running")
	if jobPaths.RunningPath() != expectedRunning {
		t.Errorf("Expected %s, got %s", expectedRunning, jobPaths.RunningPath())
	}
}

func TestJobPathsEmptyBase(t *testing.T) {
	t.Parallel() // Enable parallel execution

	jobPaths := storage.NewJobPaths("")

	// Should still work with empty base path
	expectedPending := "pending"
	if jobPaths.PendingPath() != expectedPending {
		t.Errorf("Expected %s, got %s", expectedPending, jobPaths.PendingPath())
	}

	expectedRunning := "running"
	if jobPaths.RunningPath() != expectedRunning {
		t.Errorf("Expected %s, got %s", expectedRunning, jobPaths.RunningPath())
	}
}
