package worker

import (
	"context"
	"testing"
	"time"

	"github.com/orchard9/rdev/internal/domain"
)

// mockRegistryChecker is a mock implementation of port.RegistryChecker.
type mockRegistryChecker struct {
	healthy bool
	err     string
	latency time.Duration
}

func (m *mockRegistryChecker) Check(_ context.Context) domain.RegistryStatus {
	status := domain.RegistryStatus{
		Healthy:     m.healthy,
		URL:         "https://registry.test",
		Latency:     m.latency.String(),
		LastChecked: time.Now().UTC(),
	}
	if !m.healthy {
		status.Error = m.err
	}
	return status
}

// mockExternalHealthChecker is a mock implementation of port.ExternalHealthChecker.
type mockExternalHealthChecker struct {
	system  domain.ExternalSystem
	healthy bool
	err     string
	latency time.Duration
}

func (m *mockExternalHealthChecker) Check(_ context.Context) domain.ExternalSystemStatus {
	status := domain.ExternalSystemStatus{
		System:      m.system,
		Healthy:     m.healthy,
		URL:         "https://test.system",
		Latency:     m.latency,
		LastChecked: time.Now().UTC(),
	}
	if m.healthy {
		status.LastHealthy = status.LastChecked
	} else {
		status.Error = m.err
	}
	return status
}

func TestExternalHealthChecker_GetStatus(t *testing.T) {
	registry := &mockRegistryChecker{healthy: true, latency: 50 * time.Millisecond}
	ci := &mockExternalHealthChecker{system: domain.ExternalSystemCI, healthy: true, latency: 100 * time.Millisecond}
	git := &mockExternalHealthChecker{system: domain.ExternalSystemGit, healthy: true, latency: 75 * time.Millisecond}

	checker := NewExternalHealthChecker(registry, ci, git, ExternalHealthConfig{
		CheckInterval: 100 * time.Millisecond,
	})

	// Run checks synchronously
	checker.runChecks()

	// Verify registry status
	regStatus, ok := checker.GetStatus(domain.ExternalSystemRegistry)
	if !ok {
		t.Fatal("expected registry status to exist")
	}
	if !regStatus.Healthy {
		t.Error("expected registry to be healthy")
	}

	// Verify CI status
	ciStatus, ok := checker.GetStatus(domain.ExternalSystemCI)
	if !ok {
		t.Fatal("expected CI status to exist")
	}
	if !ciStatus.Healthy {
		t.Error("expected CI to be healthy")
	}

	// Verify Git status
	gitStatus, ok := checker.GetStatus(domain.ExternalSystemGit)
	if !ok {
		t.Fatal("expected Git status to exist")
	}
	if !gitStatus.Healthy {
		t.Error("expected Git to be healthy")
	}
}

func TestExternalHealthChecker_GetAllStatuses(t *testing.T) {
	registry := &mockRegistryChecker{healthy: true}
	ci := &mockExternalHealthChecker{system: domain.ExternalSystemCI, healthy: false, err: "connection refused"}
	git := &mockExternalHealthChecker{system: domain.ExternalSystemGit, healthy: true}

	checker := NewExternalHealthChecker(registry, ci, git, ExternalHealthConfig{
		CheckInterval: 100 * time.Millisecond,
	})

	checker.runChecks()

	statuses := checker.GetAllStatuses()

	if len(statuses) != 3 {
		t.Fatalf("expected 3 statuses, got %d", len(statuses))
	}

	if statuses[domain.ExternalSystemRegistry].Healthy != true {
		t.Error("expected registry to be healthy")
	}
	if statuses[domain.ExternalSystemCI].Healthy != false {
		t.Error("expected CI to be unhealthy")
	}
	if statuses[domain.ExternalSystemCI].Error != "connection refused" {
		t.Errorf("expected CI error 'connection refused', got %q", statuses[domain.ExternalSystemCI].Error)
	}
	if statuses[domain.ExternalSystemGit].Healthy != true {
		t.Error("expected Git to be healthy")
	}
}

func TestExternalHealthChecker_NilCheckers(t *testing.T) {
	// All nil checkers should result in empty statuses
	checker := NewExternalHealthChecker(nil, nil, nil, ExternalHealthConfig{
		CheckInterval: 100 * time.Millisecond,
	})

	checker.runChecks()

	statuses := checker.GetAllStatuses()
	if len(statuses) != 0 {
		t.Fatalf("expected 0 statuses with nil checkers, got %d", len(statuses))
	}
}

func TestExternalHealthChecker_StartStop(t *testing.T) {
	registry := &mockRegistryChecker{healthy: true}

	checker := NewExternalHealthChecker(registry, nil, nil, ExternalHealthConfig{
		CheckInterval: 50 * time.Millisecond,
	})

	checker.Start()

	// Wait for a couple of check cycles
	time.Sleep(120 * time.Millisecond)

	// Verify status was populated
	status, ok := checker.GetStatus(domain.ExternalSystemRegistry)
	if !ok {
		t.Fatal("expected registry status after start")
	}
	if !status.Healthy {
		t.Error("expected registry to be healthy")
	}

	checker.Stop()

	// After stop, statuses should still be available (cached)
	status, ok = checker.GetStatus(domain.ExternalSystemRegistry)
	if !ok {
		t.Fatal("expected registry status after stop")
	}
}

func TestExternalHealthChecker_StateTransition(t *testing.T) {
	registry := &mockRegistryChecker{healthy: true}

	checker := NewExternalHealthChecker(registry, nil, nil, ExternalHealthConfig{
		CheckInterval: 100 * time.Millisecond,
	})

	// Initial check - healthy
	checker.runChecks()
	status, _ := checker.GetStatus(domain.ExternalSystemRegistry)
	if !status.Healthy {
		t.Error("expected initial status to be healthy")
	}
	firstHealthy := status.LastHealthy

	// Change to unhealthy
	registry.healthy = false
	registry.err = "connection refused"
	checker.runChecks()

	status, _ = checker.GetStatus(domain.ExternalSystemRegistry)
	if status.Healthy {
		t.Error("expected status to be unhealthy after state change")
	}
	// LastHealthy should be preserved from when it was healthy
	if status.LastHealthy.IsZero() {
		t.Error("expected LastHealthy to be preserved")
	}
	if !status.LastHealthy.Equal(firstHealthy) {
		t.Error("expected LastHealthy to remain from healthy period")
	}

	// Recover to healthy
	registry.healthy = true
	registry.err = ""
	checker.runChecks()

	status, _ = checker.GetStatus(domain.ExternalSystemRegistry)
	if !status.Healthy {
		t.Error("expected status to be healthy after recovery")
	}
	// LastHealthy should be updated
	if status.LastHealthy.Before(firstHealthy) {
		t.Error("expected LastHealthy to be updated on recovery")
	}
}

func TestExternalHealthChecker_PartialFailure(t *testing.T) {
	// Registry healthy, CI unhealthy, Git healthy
	registry := &mockRegistryChecker{healthy: true}
	ci := &mockExternalHealthChecker{system: domain.ExternalSystemCI, healthy: false, err: "timeout"}
	git := &mockExternalHealthChecker{system: domain.ExternalSystemGit, healthy: true}

	checker := NewExternalHealthChecker(registry, ci, git, ExternalHealthConfig{
		CheckInterval: 100 * time.Millisecond,
	})

	checker.runChecks()

	statuses := checker.GetAllStatuses()

	// Partial failure should not affect other systems
	if !statuses[domain.ExternalSystemRegistry].Healthy {
		t.Error("registry should be healthy despite CI failure")
	}
	if statuses[domain.ExternalSystemCI].Healthy {
		t.Error("CI should be unhealthy")
	}
	if !statuses[domain.ExternalSystemGit].Healthy {
		t.Error("git should be healthy despite CI failure")
	}
}
