package health

import (
	"context"
	"errors"
	"testing"
	"time"

	"git.dcentral.systems/toolz/goplt/pkg/health"
)

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

	registry := NewRegistry()
	if registry == nil {
		t.Fatal("Expected registry, got nil")
	}

	if registry.checkers == nil {
		t.Error("Expected checkers map, got nil")
	}
}

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

	registry := NewRegistry()

	mockChecker := &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return nil
		},
	}

	registry.Register("test", mockChecker)

	// Verify checker is registered
	registry.mu.RLock()
	checker, ok := registry.checkers["test"]
	registry.mu.RUnlock()

	if !ok {
		t.Error("Expected checker to be registered")
	}

	if checker != mockChecker {
		t.Error("Registered checker does not match")
	}
}

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

	registry := NewRegistry()

	registry.Register("healthy1", &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return nil
		},
	})

	registry.Register("healthy2", &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return nil
		},
	})

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	status := registry.Check(ctx)

	if status.Status != health.StatusHealthy {
		t.Errorf("Expected status healthy, got %s", status.Status)
	}

	if len(status.Components) != 2 {
		t.Errorf("Expected 2 components, got %d", len(status.Components))
	}

	for _, component := range status.Components {
		if component.Status != health.StatusHealthy {
			t.Errorf("Expected component %s to be healthy, got %s", component.Name, component.Status)
		}
	}
}

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

	registry := NewRegistry()

	registry.Register("healthy", &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return nil
		},
	})

	registry.Register("unhealthy", &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return errors.New("component failed")
		},
	})

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	status := registry.Check(ctx)

	if status.Status != health.StatusUnhealthy {
		t.Errorf("Expected status unhealthy, got %s", status.Status)
	}

	if len(status.Components) != 2 {
		t.Errorf("Expected 2 components, got %d", len(status.Components))
	}

	unhealthyFound := false
	for _, component := range status.Components {
		if component.Name == "unhealthy" {
			unhealthyFound = true
			if component.Status != health.StatusUnhealthy {
				t.Errorf("Expected unhealthy component to be unhealthy, got %s", component.Status)
			}
			if component.Error == "" {
				t.Error("Expected error message for unhealthy component")
			}
		}
	}

	if !unhealthyFound {
		t.Error("Expected to find unhealthy component")
	}
}

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

	registry := NewRegistry()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	status := registry.LivenessCheck(ctx)

	if status.Status != health.StatusHealthy {
		t.Errorf("Expected liveness check to be healthy, got %s", status.Status)
	}

	if len(status.Components) != 0 {
		t.Errorf("Expected no components in liveness check, got %d", len(status.Components))
	}
}

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

	registry := NewRegistry()

	registry.Register("test", &mockChecker{
		checkFunc: func(ctx context.Context) error {
			return nil
		},
	})

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	status := registry.ReadinessCheck(ctx)

	if status.Status != health.StatusHealthy {
		t.Errorf("Expected readiness check to be healthy, got %s", status.Status)
	}

	if len(status.Components) != 1 {
		t.Errorf("Expected 1 component in readiness check, got %d", len(status.Components))
	}
}

// mockChecker is a mock implementation of HealthChecker for testing.
type mockChecker struct {
	checkFunc func(ctx context.Context) error
}

func (m *mockChecker) Check(ctx context.Context) error {
	if m.checkFunc != nil {
		return m.checkFunc(ctx)
	}
	return nil
}
