package model

import (
	"fmt"
	"testing"
	"time"

	"github.com/onsi/gomega"
	"gorm.io/gorm"

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

func TestFetchPendingOrRunningHealthCheckJobsBatchAndCount(t *testing.T) {
	g := gomega.NewWithT(t)
	prefix := fmt.Sprintf("healthcheck-batch-%d", time.Now().UnixNano())
	base := time.Now().Add(-time.Hour)

	results := []*db.HealthCheckResult{
		newHealthCheckBatchResult(prefix+"-old-pending", db.StatusPending, base.Add(0*time.Minute)),
		newHealthCheckBatchResult(prefix+"-success", db.StatusSuccess, base.Add(1*time.Minute)),
		newHealthCheckBatchResult(prefix+"-running", db.StatusRunning, base.Add(2*time.Minute)),
		newHealthCheckBatchResult(prefix+"-partial", db.StatusPartial, base.Add(3*time.Minute)),
		newHealthCheckBatchResult(prefix+"-new-pending", db.StatusPending, base.Add(4*time.Minute)),
	}
	for _, result := range results {
		g.Expect(InsertHealthCheckResult(result)).To(gomega.Succeed())
	}
	t.Cleanup(func() {
		for _, result := range results {
			_ = DeleteHealthCheckResult(result.UUID)
		}
	})

	count, err := CountPendingOrRunningHealthCheckJobs([]string{prefix + "-old-pending"})
	g.Expect(err).To(gomega.Succeed())
	g.Expect(count).To(gomega.Equal(int64(3)))

	var batch []*db.HealthCheckResult
	err = FetchPendingOrRunningHealthCheckJobsBatch(&batch, 2, []string{prefix + "-old-pending"})
	g.Expect(err).To(gomega.Succeed())
	g.Expect(batch).To(gomega.HaveLen(2))
	g.Expect(batch[0].UUID).To(gomega.Equal(prefix + "-new-pending"))
	g.Expect(batch[1].UUID).To(gomega.Equal(prefix + "-running"))
}

func TestHealthCheckReportQueriesEnforceScopeAndFilters(t *testing.T) {
	g := gomega.NewWithT(t)
	prefix := fmt.Sprintf("%x", time.Now().UnixNano())
	base := time.Now().UTC().Add(-time.Hour)
	results := []*db.HealthCheckResult{
		newScopedHealthCheckResult(prefix+"-success", "org-a", "cluster-a", db.StatusSuccess, base),
		newScopedHealthCheckResult(prefix+"-failed", "org-a", "cluster-a", db.StatusFailed, base.Add(time.Minute)),
		newScopedHealthCheckResult(prefix+"-other-org", "org-b", "cluster-a", db.StatusSuccess, base.Add(2*time.Minute)),
		newScopedHealthCheckResult(prefix+"-other-cluster", "org-a", "cluster-b", db.StatusSuccess, base.Add(3*time.Minute)),
	}
	for _, result := range results {
		g.Expect(InsertHealthCheckResult(result)).To(gomega.Succeed())
	}
	t.Cleanup(func() {
		for _, result := range results {
			_ = DeleteHealthCheckResult(result.UUID)
		}
	})

	report, err := GetHealthCheckResultByScope("org-a", "cluster-a", prefix+"-success")
	g.Expect(err).To(gomega.Succeed())
	g.Expect(report.UUID).To(gomega.Equal(prefix + "-success"))
	_, err = GetHealthCheckResultByScope("org-b", "cluster-a", prefix+"-success")
	g.Expect(err).To(gomega.MatchError(gorm.ErrRecordNotFound))
	_, err = GetHealthCheckResultByScope("org-a", "cluster-b", prefix+"-success")
	g.Expect(err).To(gomega.MatchError(gorm.ErrRecordNotFound))

	createdTo := base.Add(30 * time.Second)
	page, total, err := ListHealthCheckResultsByScope("org-a", "cluster-a", HealthCheckResultListOptions{
		Page: 1, PageSize: 20, Statuses: []string{string(db.StatusSuccess)}, CreatedTo: &createdTo,
	})
	g.Expect(err).To(gomega.Succeed())
	g.Expect(total).To(gomega.Equal(int64(1)))
	g.Expect(page).To(gomega.HaveLen(1))
	g.Expect(page[0].UUID).To(gomega.Equal(prefix + "-success"))
}

func newHealthCheckBatchResult(uuid string, status db.StatusType, createdAt time.Time) *db.HealthCheckResult {
	return &db.HealthCheckResult{
		Model: gorm.Model{
			CreatedAt: createdAt,
		},
		UUID:         uuid,
		StrategyUUID: uuid + "-strategy",
		ClusterID:    "healthcheck-batch-cluster",
		DbaasOrgID:   "healthcheck-batch-org",
		ItemsToRun:   "[]",
		Status:       string(status),
	}
}

func newScopedHealthCheckResult(
	uuid, orgID, clusterID string,
	status db.StatusType,
	createdAt time.Time,
) *db.HealthCheckResult {
	return &db.HealthCheckResult{
		Model:        gorm.Model{CreatedAt: createdAt},
		UUID:         uuid,
		StrategyUUID: uuid + "-strategy",
		ClusterID:    clusterID,
		DbaasOrgID:   orgID,
		ItemsToRun:   "[]",
		Status:       string(status),
	}
}
