package auth

import (
	"os/exec"
	"strings"
	"testing"

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

// isKeyringAvailable checks if dbus is available for keyring operations
func isKeyringAvailable() bool {
	// Check if dbus-launch is available (Linux keyring requirement)
	if _, err := exec.LookPath("dbus-launch"); err != nil {
		return false
	}
	return true
}

func TestNewKeychainManager(t *testing.T) {
	t.Parallel()

	km := auth.NewKeychainManager()
	if km == nil {
		t.Fatal("NewKeychainManager returned nil")
	}

	methods := km.ListAvailableMethods()
	if len(methods) == 0 {
		t.Error("Expected at least one available method")
	}
}

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

	km := auth.NewKeychainManager()

	// IsAvailable should return a boolean without error
	available := km.IsAvailable()

	// We can't predict the result since it depends on the test environment,
	// but it should not panic
	t.Logf("Keychain availability: %v", available)
}

func TestKeychainBasicOperations(t *testing.T) {
	t.Parallel()

	if !isKeyringAvailable() {
		t.Skip("Skipping: dbus-launch not available for keyring operations")
	}

	km := auth.NewKeychainManager()

	service := "test-service"
	account := "test-account"
	secret := "test-secret"

	// Test storing API key
	if err := km.StoreAPIKey(service, account, secret); err != nil {
		t.Fatalf("StoreAPIKey failed: %v", err)
	}

	// Test retrieving API key
	retrieved, err := km.GetAPIKey(service, account)
	if err != nil {
		t.Fatalf("GetAPIKey failed: %v", err)
	}
	if retrieved != secret {
		t.Errorf("Expected secret %s, got %s", secret, retrieved)
	}

	// Test deleting API key
	if err := km.DeleteAPIKey(service, account); err != nil {
		t.Fatalf("DeleteAPIKey failed: %v", err)
	}

	// Verify deletion - should fail to retrieve
	_, err = km.GetAPIKey(service, account)
	if err == nil {
		t.Error("Expected error when retrieving deleted key")
	}
}

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

	km := auth.NewKeychainManager()
	methods := km.ListAvailableMethods()

	if len(methods) == 0 {
		t.Error("Expected at least one available method")
	}

	// Check that fallback method is always included
	hasFallback := false
	for _, method := range methods {
		if method == "OS keyring" {
			// OS keyring might be available
			continue
		}
		if len(method) > 0 {
			hasFallback = true
			break
		}
	}

	if !hasFallback {
		t.Error("Expected fallback method to be available")
	}

	t.Logf("Available methods: %v", methods)
}

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

	km := auth.NewKeychainManager()

	// Test getting non-existent key
	_, err := km.GetAPIKey("non-existent", "non-existent")
	if err == nil {
		t.Error("Expected error when getting non-existent key")
	}

	// Test deleting non-existent key (should not error)
	if err := km.DeleteAPIKey("non-existent", "non-existent"); err != nil {
		t.Errorf("DeleteAPIKey should not error for non-existent key: %v", err)
	}
}

func TestKeychainMultipleKeys(t *testing.T) {
	t.Parallel()

	if !isKeyringAvailable() {
		t.Skip("Skipping: dbus-launch not available for keyring operations")
	}

	km := auth.NewKeychainManager()

	keys := map[string]string{
		"service1:account1": "secret1",
		"service1:account2": "secret2",
		"service2:account1": "secret3",
	}

	// Store multiple keys
	for serviceAccount, secret := range keys {
		parts := strings.SplitN(serviceAccount, ":", 2)
		service, account := parts[0], parts[1]

		if err := km.StoreAPIKey(service, account, secret); err != nil {
			t.Fatalf("StoreAPIKey failed for %s: %v", serviceAccount, err)
		}
	}

	// Retrieve and verify all keys
	for serviceAccount, expectedSecret := range keys {
		parts := strings.SplitN(serviceAccount, ":", 2)
		service, account := parts[0], parts[1]

		retrieved, err := km.GetAPIKey(service, account)
		if err != nil {
			t.Fatalf("GetAPIKey failed for %s: %v", serviceAccount, err)
		}
		if retrieved != expectedSecret {
			t.Errorf("Expected secret %s for %s, got %s", expectedSecret, serviceAccount, retrieved)
		}

		// Clean up each key
		if err := km.DeleteAPIKey(service, account); err != nil {
			t.Fatalf("DeleteAPIKey failed for %s: %v", serviceAccount, err)
		}
	}
}
