package healthcheck

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/onsi/gomega"

	"github.com/pingcap-inc/clinic/pkg/api/healthcheck"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/db/model"
)

func TestNewScannerService(t *testing.T) {
	g := gomega.NewWithT(t)

	scanner := NewScannerService(nil)
	g.Expect(scanner).NotTo(gomega.BeNil())
	g.Expect(scanner.queue).NotTo(gomega.BeNil())
}

func TestScannerScanPendingJobs(t *testing.T) {
	g := gomega.NewWithT(t)
	repo := model.NewMockHealthCheckRepository()
	useHealthCheckRepository(t, repo)

	resultUUID := uuid.NewString()
	clusterID := "HealthCheck_Test_Cluster_0"
	strategyUUID := uuid.NewString()
	items := []healthcheck.CheckItem{
		{
			Name:      "Test Item",
			CheckType: "sql",
			Enabled:   true,
		},
	}
	itemsJSON, _ := json.Marshal(items)

	strategy := &db.HealthCheckStrategy{
		UUID:       strategyUUID,
		ClusterID:  clusterID,
		DbaasOrgID: "1",
		CreatorID:  1,
		Name:       "Test Strategy",
		Status:     "active",
		Items:      string(itemsJSON),
	}
	repo.Strategies[strategy.UUID] = strategy

	startTime := time.Now().Add(-24 * time.Hour)
	endTime := time.Now()

	result := &db.HealthCheckResult{
		UUID:         resultUUID,
		StrategyUUID: strategyUUID,
		ClusterID:    clusterID,
		DbaasOrgID:   "1",
		ItemsToRun:   "Test Item",
		Status:       string(db.StatusPending),

		TimeRangeAStart: &startTime,
		TimeRangeAEnd:   &endTime,
	}
	repo.Results[result.UUID] = result

	scanner := NewScannerService(nil)
	g.Expect(scanner).NotTo(gomega.BeNil())
	scanner.scanPendingJobs()

	g.Expect(scanner.queue).To(gomega.HaveLen(1))
	job := (<-scanner.queue).(*Job)
	g.Expect(job.result.UUID).To(gomega.Equal(resultUUID))
}

func TestScannerScanCronStrategies(t *testing.T) {
	g := gomega.NewWithT(t)
	repo := model.NewMockHealthCheckRepository()
	useHealthCheckRepository(t, repo)

	// Create a cron strategy
	strategyUUID := uuid.NewString()
	clusterID := "HealthCheck_Test_Cluster_1"

	items := []healthcheck.CheckItem{
		{
			Name:        "Cron Test Item",
			Description: "Test cron item",
			CheckType:   "sql",
			Enabled:     true,
		},
	}
	itemsJSON, _ := json.Marshal(items)

	strategy := &db.HealthCheckStrategy{
		UUID:         strategyUUID,
		ClusterID:    clusterID,
		DbaasOrgID:   "1",
		CreatorID:    1,
		Name:         "Cron Test Strategy",
		Description:  "Test cron strategy",
		ScheduleType: "cron",
		ScheduleExpr: "0 * * * *", // Every hour
		Status:       "active",
		Items:        string(itemsJSON),
		NextRunAt:    func() *time.Time { t := time.Now().Add(-time.Minute); return &t }(), // Past time, should trigger
	}
	repo.Strategies[strategy.UUID] = strategy

	// Create scanner and test scan
	scanner := NewScannerService(nil)
	g.Expect(scanner).NotTo(gomega.BeNil())

	// Scan cron strategies
	scanner.scanCronStrategies()

	found := false
	for _, r := range repo.Results {
		if r.StrategyUUID == strategyUUID && r.ItemsToRun != "" {
			var items []healthcheck.CheckItem
			g.Expect(json.Unmarshal([]byte(r.ItemsToRun), &items)).To(gomega.Succeed())
			for _, item := range items {
				if item.Name == "Cron Test Item" {
					found = true
					break
				}
			}
		}
		if found {
			break
		}
	}
	g.Expect(found).To(gomega.BeTrue())
	g.Expect(repo.Strategies[strategyUUID].LastRunAt).NotTo(gomega.BeNil())
	g.Expect(repo.Strategies[strategyUUID].NextRunAt).NotTo(gomega.BeNil())
}

func TestCalculateNextRunTime(t *testing.T) {
	g := gomega.NewWithT(t)

	now := time.Now()
	nextRun := calculateNextRunTime("0 * * * *")

	g.Expect(nextRun).NotTo(gomega.BeNil())
	g.Expect(*nextRun).To(gomega.BeTemporally(">", now))
}

// BenchmarkScanPendingJobs 基准测试扫描性能
func BenchmarkScanPendingJobs(b *testing.B) {
	repo := model.NewMockHealthCheckRepository()
	SetRepository(repo)
	b.Cleanup(func() { SetRepository(model.NewHealthCheckDB()) })

	strategyUUID := uuid.NewString()

	for i := 0; i < 100; i++ {
		resultUUID := uuid.NewString()

		startTime := time.Now().Add(-24 * time.Hour)
		endTime := time.Now()

		result := &db.HealthCheckResult{
			UUID:         resultUUID,
			StrategyUUID: strategyUUID,
			ClusterID:    "Benchmark_Cluster",
			DbaasOrgID:   "1",
			ItemsToRun:   "Benchmark Item",
			Status:       string(db.StatusPending),

			TimeRangeAStart: &startTime,
			TimeRangeAEnd:   &endTime,
		}
		repo.Results[result.UUID] = result
	}

	scanner := NewScannerService(nil)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		scanner.scanPendingJobs()
		for len(scanner.queue) > 0 {
			job := (<-scanner.queue).(*Job)
			scanner.releaseInFlight(job.result.UUID)
		}
	}
}

// TestScannerStart 测试扫描器启动
func TestScannerStart(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	g := gomega.NewWithT(t)

	scanner := NewScannerService(nil)
	g.Expect(scanner).NotTo(gomega.BeNil())

	// Start scanner (will run in background)
	scanner.Start()

	// Give it a moment to start
	time.Sleep(100 * time.Millisecond)
}
