package secrets

import (
	"context"
	"os"
	"path/filepath"
	"testing"
	"time"
)

// TestSOPSBackendList tests listing SOPS secrets from a mock directory.
func TestSOPSBackendList(t *testing.T) {
	dir := t.TempDir()

	// Create a mock SOPS-encrypted YAML (plaintext for testing since sops may not be installed)
	os.WriteFile(filepath.Join(dir, "test.yaml"), []byte("key1: value1\nkey2: value2\n"), 0644)

	backend := NewSOPSBackend(dir)
	// Without sops binary, load will fail for encrypted files.
	// Test that the backend handles missing sops gracefully.
	// For actual tests with sops installed, see integration tests.
	_ = backend
	t.Log("SOPS backend created (requires sops binary for integration tests)")
}

// TestManagerFallback verifies fallback from primary to secondary backend.
func TestManagerFallback(t *testing.T) {
	ctx := context.Background()

	primary := &mockBackend{name: "primary", data: map[string]string{}, getErr: ErrBackendUnavailable}
	fallback := &mockBackend{name: "fallback", data: map[string]string{"test": "fallback-value"}}

	mgr := NewManager(primary, fallback)

	val, err := mgr.Get(ctx, "test")
	if err != nil {
		t.Fatalf("expected fallback to provide value, got: %v", err)
	}
	if val != "fallback-value" {
		t.Errorf("val = %q, want fallback-value", val)
	}
}

// TestManagerCache tests in-memory secret caching.
func TestManagerCache(t *testing.T) {
	ctx := context.Background()

	primary := &mockBackend{name: "primary", data: map[string]string{"cached": "v1"}}
	mgr := NewManager(primary, nil)
	mgr.cacheTTL = 60 * time.Second // don't expire during test

	// First call: hits backend
	val, err := mgr.Get(ctx, "cached")
	if err != nil {
		t.Fatalf("first get: %v", err)
	}
	if val != "v1" {
		t.Errorf("first get = %q, want v1", val)
	}

	// Change the value in backend
	primary.data["cached"] = "v2"

	// Second call: should hit cache, return old value
	val, _ = mgr.Get(ctx, "cached")
	if val != "v1" {
		t.Errorf("cached get = %q, want v1 (cached)", val)
	}

	// Invalidate cache, should get new value
	mgr.InvalidateCache()
	val, _ = mgr.Get(ctx, "cached")
	if val != "v2" {
		t.Errorf("after invalidate = %q, want v2", val)
	}
}

// TestManagerPrimaryName returns the backend identifier.
func TestManagerPrimaryName(t *testing.T) {
	mgr := NewManager(&mockBackend{name: "infisical"}, nil)
	if mgr.PrimaryName() != "infisical" {
		t.Errorf("name = %q, want infisical", mgr.PrimaryName())
	}
}

// mockBackend implements Backend for testing.
type mockBackend struct {
	name   string
	data   map[string]string
	getErr error
}

func (m *mockBackend) Name() string { return m.name }
func (m *mockBackend) Get(ctx context.Context, key string) (string, error) {
	if m.getErr != nil {
		return "", m.getErr
	}
	v, ok := m.data[key]
	if !ok {
		return "", ErrNotFound
	}
	return v, nil
}
func (m *mockBackend) List(ctx context.Context) ([]string, error) {
	keys := make([]string, 0, len(m.data))
	for k := range m.data {
		keys = append(keys, k)
	}
	return keys, nil
}
func (m *mockBackend) Set(ctx context.Context, key string, value string) error {
	m.data[key] = value
	return nil
}
