package service

import (
	"context"
	"testing"
	"time"

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

// mockOperationRepo implements port.OperationRepository for testing.
type mockOperationRepo struct {
	operations []*domain.Operation
	err        error
}

func (m *mockOperationRepo) Create(_ context.Context, _ *domain.Operation) error {
	return nil
}

func (m *mockOperationRepo) Update(_ context.Context, _ *domain.Operation) error {
	return nil
}

func (m *mockOperationRepo) Get(_ context.Context, _ string) (*domain.Operation, error) {
	return nil, domain.ErrOperationNotFound
}

func (m *mockOperationRepo) GetByCommitSHA(_ context.Context, _, _ string) (*domain.Operation, error) {
	return nil, domain.ErrOperationNotFound
}

func (m *mockOperationRepo) List(_ context.Context, filter domain.OperationFilters) ([]*domain.Operation, error) {
	if m.err != nil {
		return nil, m.err
	}
	var result []*domain.Operation
	for _, op := range m.operations {
		if filter.ProjectID != "" && op.ProjectID != filter.ProjectID {
			continue
		}
		result = append(result, op)
	}
	return result, nil
}

func (m *mockOperationRepo) AddStep(_ context.Context, _ string, _ domain.OperationStep) error {
	return nil
}

func (m *mockOperationRepo) UpdateStep(_ context.Context, _ string, _ domain.OperationStep) error {
	return nil
}

func (m *mockOperationRepo) Complete(_ context.Context, _ string, _ domain.OperationStatus, _ map[string]any, _, _ string) error {
	return nil
}

func (m *mockOperationRepo) SetCommitSHA(_ context.Context, _, _ string) error {
	return nil
}

func (m *mockOperationRepo) SetTriggeredBy(_ context.Context, _, _ string) error {
	return nil
}

func (m *mockOperationRepo) DeleteOlderThan(_ context.Context, _ time.Time) (int64, error) {
	return 0, nil
}

// mockRegistryChecker implements port.RegistryChecker for testing.
type mockRegistryChecker struct {
	status domain.RegistryStatus
}

func (m *mockRegistryChecker) Check(_ context.Context) domain.RegistryStatus {
	return m.status
}

// mockCIProvider implements port.CIProvider for testing.
type mockCIProvider struct {
	pipelines []*domain.CIPipeline
	steps     *domain.CIPipelineSteps
	err       error
}

func (m *mockCIProvider) ActivateRepo(_ context.Context, _, _, _ string) (*domain.CIRepo, error) {
	return nil, nil
}

func (m *mockCIProvider) DeactivateRepo(_ context.Context, _, _ string) error {
	return nil
}

func (m *mockCIProvider) GetRepo(_ context.Context, _, _ string) (*domain.CIRepo, error) {
	return nil, nil
}

func (m *mockCIProvider) ListRepos(_ context.Context) ([]*domain.CIRepo, error) {
	return nil, nil
}

func (m *mockCIProvider) AddSecret(_ context.Context, _, _ string, _ domain.CISecret) error {
	return nil
}

func (m *mockCIProvider) DeleteSecret(_ context.Context, _, _, _ string) error {
	return nil
}

func (m *mockCIProvider) ListPipelines(_ context.Context, _, _ string) ([]*domain.CIPipeline, error) {
	if m.err != nil {
		return nil, m.err
	}
	return m.pipelines, nil
}

func (m *mockCIProvider) GetPipeline(_ context.Context, _, _ string, _ int64) (*domain.CIPipeline, error) {
	return nil, nil
}

func (m *mockCIProvider) GetPipelineSteps(_ context.Context, _, _ string, _ int64) (*domain.CIPipelineSteps, error) {
	if m.steps != nil {
		return m.steps, nil
	}
	return nil, nil
}

func (m *mockCIProvider) TriggerBuild(_ context.Context, _, _, _ string) (int64, error) {
	return 0, nil
}

func (m *mockCIProvider) RetryPipeline(_ context.Context, _, _ string, _ int64) (*domain.CIPipeline, error) {
	return nil, nil
}

func TestDiagnosticsService_GetDiagnostics_Healthy(t *testing.T) {
	opRepo := &mockOperationRepo{
		operations: []*domain.Operation{
			{
				ID:        "op-1",
				ProjectID: "test-project",
				Type:      domain.OperationTypeBuild,
				Status:    domain.OperationStatusCompleted,
				StartedAt: time.Now().Add(-1 * time.Hour),
			},
		},
	}
	registry := &mockRegistryChecker{
		status: domain.RegistryStatus{
			Healthy:     true,
			URL:         "https://registry.example.com",
			Latency:     "10ms",
			LastChecked: time.Now(),
		},
	}
	ci := &mockCIProvider{
		pipelines: []*domain.CIPipeline{
			{
				Number: 42,
				Status: "success",
				Branch: "main",
				Commit: "abc123",
			},
		},
	}

	svc := NewDiagnosticsService(opRepo, registry, ci, DiagnosticsServiceConfig{
		DefaultGitOwner: "test-org",
	})

	diag, err := svc.GetDiagnostics(context.Background(), "test-project")
	if err != nil {
		t.Fatalf("GetDiagnostics() error = %v", err)
	}

	if diag.ProjectID != "test-project" {
		t.Errorf("ProjectID = %q, want %q", diag.ProjectID, "test-project")
	}
	if diag.Summary != domain.DiagnosticsSummaryHealthy {
		t.Errorf("Summary = %q, want %q", diag.Summary, domain.DiagnosticsSummaryHealthy)
	}
	if len(diag.Issues) != 0 {
		t.Errorf("Issues count = %d, want 0", len(diag.Issues))
	}
	if len(diag.RecentOperations) != 1 {
		t.Errorf("RecentOperations count = %d, want 1", len(diag.RecentOperations))
	}
	if diag.Registry == nil {
		t.Error("Registry is nil, want non-nil")
	}
	if diag.CI == nil || !diag.CI.Available {
		t.Error("CI not available")
	}
}

func TestDiagnosticsService_GetDiagnostics_Unhealthy(t *testing.T) {
	opRepo := &mockOperationRepo{
		operations: []*domain.Operation{
			{
				ID:        "op-1",
				ProjectID: "test-project",
				Type:      domain.OperationTypeBuild,
				Status:    domain.OperationStatusFailed,
				StartedAt: time.Now().Add(-1 * time.Hour),
				Error:     "deployment failed",
			},
		},
	}
	registry := &mockRegistryChecker{
		status: domain.RegistryStatus{
			Healthy:     false,
			URL:         "https://registry.example.com",
			Error:       "connection refused",
			LastChecked: time.Now(),
		},
	}
	ci := &mockCIProvider{
		pipelines: []*domain.CIPipeline{
			{
				Number:   43,
				Status:   "failure",
				Branch:   "main",
				Commit:   "def456",
				Finished: time.Now().Add(-30 * time.Minute),
			},
		},
		steps: &domain.CIPipelineSteps{
			PipelineNumber: 43,
			URL:            "https://ci.example.com/build/43",
			Steps: []domain.CIPipelineStep{
				{
					Name:   "test",
					Status: "failure",
					Error:  "tests failed",
					Log:    "FAIL: TestSomething",
				},
			},
		},
	}

	svc := NewDiagnosticsService(opRepo, registry, ci, DiagnosticsServiceConfig{
		DefaultGitOwner: "test-org",
	})

	diag, err := svc.GetDiagnostics(context.Background(), "test-project")
	if err != nil {
		t.Fatalf("GetDiagnostics() error = %v", err)
	}

	if diag.Summary != domain.DiagnosticsSummaryUnhealthy {
		t.Errorf("Summary = %q, want %q", diag.Summary, domain.DiagnosticsSummaryUnhealthy)
	}

	// Should have 3 issues: failed operation, unhealthy registry, failed CI
	if len(diag.Issues) < 3 {
		t.Errorf("Issues count = %d, want at least 3", len(diag.Issues))
	}

	// Check CI failure details
	if diag.CI == nil || diag.CI.LastFailure == nil {
		t.Fatal("CI.LastFailure is nil")
	}
	if diag.CI.LastFailure.FailedStep != "test" {
		t.Errorf("FailedStep = %q, want %q", diag.CI.LastFailure.FailedStep, "test")
	}
}

func TestDiagnosticsService_GetDiagnostics_Degraded(t *testing.T) {
	opRepo := &mockOperationRepo{
		operations: []*domain.Operation{
			{
				ID:        "op-1",
				ProjectID: "test-project",
				Type:      domain.OperationTypeBuild,
				Status:    domain.OperationStatusCompleted,
				StartedAt: time.Now().Add(-1 * time.Hour),
			},
		},
	}
	registry := &mockRegistryChecker{
		status: domain.RegistryStatus{
			Healthy:     true,
			URL:         "https://registry.example.com",
			LastChecked: time.Now(),
		},
	}

	// No CI provider - should produce a warning but not error
	svc := NewDiagnosticsService(opRepo, registry, nil, DiagnosticsServiceConfig{
		DefaultGitOwner: "test-org",
	})

	diag, err := svc.GetDiagnostics(context.Background(), "test-project")
	if err != nil {
		t.Fatalf("GetDiagnostics() error = %v", err)
	}

	// Without CI, status should still be healthy (CI unavailable is not an issue)
	if diag.Summary != domain.DiagnosticsSummaryHealthy {
		t.Errorf("Summary = %q, want %q", diag.Summary, domain.DiagnosticsSummaryHealthy)
	}
	if diag.CI == nil || diag.CI.Available {
		t.Error("CI should be not available when no provider")
	}
}
