package service_test

import (
	"testing"
	"time"

	"github.com/pingcap/agent-git-service/internal/db"
	"github.com/pingcap/agent-git-service/internal/metrics"
	"github.com/pingcap/agent-git-service/internal/testharness"

	"github.com/prometheus/client_golang/prometheus/testutil"
)

func TestBusinessMetricsActivity_RecordsDailyAndFirstPushOnce(t *testing.T) {
	svc, tenantHandle, cleanup := testharness.NewService(t, testharness.ServiceConfig{})
	defer cleanup()

	now := time.Now().UTC()
	user := db.User{
		Login:     "business-metrics-user",
		Name:      "business-metrics-user",
		Type:      db.TypeUser,
		UserKind:  db.UserKindHuman,
		CreatedAt: now.Add(-2 * time.Hour),
	}
	if err := tenantHandle.DB().Create(&user).Error; err != nil {
		t.Fatalf("create user: %v", err)
	}
	repo := db.Repository{
		Name:          "business-metrics-repo",
		FullName:      user.Login + "/business-metrics-repo",
		OwnerID:       user.ID,
		DefaultBranch: "main",
		CreatedAt:     now.Add(-1 * time.Hour),
		UpdatedAt:     now.Add(-1 * time.Hour),
	}
	if err := tenantHandle.DB().Create(&repo).Error; err != nil {
		t.Fatalf("create repo: %v", err)
	}

	ctx := testContextWithUser(tenantHandle.Context(), user)
	if err := svc.UpdateRepositoryPushedAt(ctx, repo.ID, now); err != nil {
		t.Fatalf("first UpdateRepositoryPushedAt: %v", err)
	}
	if err := svc.UpdateRepositoryPushedAt(ctx, repo.ID, now.Add(time.Minute)); err != nil {
		t.Fatalf("second UpdateRepositoryPushedAt: %v", err)
	}

	day := now.UTC().Format("2006-01-02")
	assertBusinessCount(t, tenantHandle, &db.BusinessUserActivityDaily{}, 1, "day = ? AND user_id = ?", day, user.ID)
	assertBusinessCount(t, tenantHandle, &db.BusinessRepoActivityDaily{}, 1, "day = ? AND repository_id = ?", day, repo.ID)
	assertBusinessCount(t, tenantHandle, &db.BusinessFirstUserAction{}, 1, "user_id = ? AND action = ?", user.ID, "git_push")
	assertBusinessCount(t, tenantHandle, &db.BusinessFirstRepoAction{}, 1, "repository_id = ? AND action = ?", repo.ID, "git_push")
}

func TestBusinessMetricsActivity_DeleteRepoRefreshesActiveRepoGauge(t *testing.T) {
	metrics.Init()

	svc, tenantHandle, cleanup := testharness.NewService(t, testharness.ServiceConfig{})
	defer cleanup()

	now := time.Now().UTC()
	day := now.Format("2006-01-02")

	owner := db.User{Login: "repo-gauge-owner", Name: "repo-gauge-owner", Type: db.TypeUser}
	other := db.User{Login: "repo-gauge-other", Name: "repo-gauge-other", Type: db.TypeUser}
	if err := tenantHandle.DB().Create(&owner).Error; err != nil {
		t.Fatalf("create owner: %v", err)
	}
	if err := tenantHandle.DB().Create(&other).Error; err != nil {
		t.Fatalf("create other: %v", err)
	}

	repo := db.Repository{Name: "repo-gauge", FullName: owner.Login + "/repo-gauge", OwnerID: owner.ID, DefaultBranch: "main", CreatedAt: now, UpdatedAt: now}
	otherRepo := db.Repository{Name: "repo-gauge-other", FullName: other.Login + "/repo-gauge-other", OwnerID: other.ID, DefaultBranch: "main", CreatedAt: now, UpdatedAt: now}
	if err := tenantHandle.DB().Create(&repo).Error; err != nil {
		t.Fatalf("create repo: %v", err)
	}
	if err := tenantHandle.DB().Create(&otherRepo).Error; err != nil {
		t.Fatalf("create other repo: %v", err)
	}

	if err := tenantHandle.DB().Create(&db.BusinessRepoActivityDaily{Day: day, RepositoryID: repo.ID}).Error; err != nil {
		t.Fatalf("create business activity for repo: %v", err)
	}
	if err := tenantHandle.DB().Create(&db.BusinessRepoActivityDaily{Day: day, RepositoryID: otherRepo.ID}).Error; err != nil {
		t.Fatalf("create business activity for other repo: %v", err)
	}

	ownerCtx := testContextWithUser(tenantHandle.Context(), owner)
	otherCtx := testContextWithUser(tenantHandle.Context(), other)
	tenantID := string(tenantHandle.Info().ID)

	if err := svc.UpdateRepositoryPushedAt(ownerCtx, repo.ID, now); err != nil {
		t.Fatalf("record push for repo: %v", err)
	}
	if err := svc.UpdateRepositoryPushedAt(otherCtx, otherRepo.ID, now); err != nil {
		t.Fatalf("record push for other repo: %v", err)
	}

	if got := testutil.ToFloat64(metrics.DefaultPrometheus().ActiveReposDaily.WithLabelValues(tenantID)); got != 2 {
		t.Fatalf("active_repos_daily before delete: got %v, want 2", got)
	}

	if err := svc.DeleteRepo(ownerCtx, repo.FullName); err != nil {
		t.Fatalf("delete repo: %v", err)
	}

	if got := testutil.ToFloat64(metrics.DefaultPrometheus().ActiveReposDaily.WithLabelValues(tenantID)); got != 1 {
		t.Fatalf("active_repos_daily after delete: got %v, want 1", got)
	}
}

func assertBusinessCount(t *testing.T, tenantHandle *testharness.TenantHandle, model any, want int64, query string, args ...any) {
	t.Helper()
	var got int64
	if err := tenantHandle.DB().Model(model).Where(query, args...).Count(&got).Error; err != nil {
		t.Fatalf("count %T: %v", model, err)
	}
	if got != want {
		t.Fatalf("count %T: got %d, want %d", model, got, want)
	}
}
