//go:build test
// +build test

package bookings

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"crussell/db"
	"crussell/mw"
	"crussell/testutils/fixtures"

	"github.com/go-chi/chi/v5"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func makeProgressRequest(handler http.HandlerFunc, method, path string, body interface{}, token string) *httptest.ResponseRecorder {
	var req *http.Request
	if body != nil {
		bodyBytes, _ := json.Marshal(body)
		req = httptest.NewRequest(method, path, bytes.NewReader(bodyBytes))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req = httptest.NewRequest(method, path, nil)
	}
	if token != "" {
		req.Header.Set("Authorization", "Bearer "+token)
	}

	rctx := chi.NewRouteContext()
	parts := strings.Split(strings.Trim(path, "/"), "/")
	for i, p := range parts {
		if p == "bookings" && i+1 < len(parts) {
			rctx.URLParams.Add("id", parts[i+1])
			break
		}
	}
	ctx := context.WithValue(req.Context(), chi.RouteCtxKey, rctx)

	if token != "" {
		ctx = context.WithValue(ctx, mw.UserIDKey, "admin-test-001")
		ctx = context.WithValue(ctx, mw.UserRoleKey, "admin")
	}

	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	handler.ServeHTTP(w, req)
	return w
}

func createTestUser(t *testing.T, stamps int) string {
	t.Helper()
	userID, err := fixtures.CreateTestUser(db.DB)
	require.NoError(t, err)

	if stamps > 0 {
		_, err := db.DB.Exec(context.Background(), "UPDATE users SET loyalty_stamps = $1 WHERE id = $2", stamps, userID)
		require.NoError(t, err)
	}

	return userID
}

func createTestService(t *testing.T, price float64) string {
	t.Helper()
	ctx := context.Background()
	var serviceID string
	err := db.DB.QueryRow(ctx, `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, "Test Service", "A test service", price, 60, true, 16).Scan(&serviceID)
	require.NoError(t, err)
	return serviceID
}

func createTestCampaign(t *testing.T, name, campaignType string, percent float64, milestoneType, milestoneUnit *string, milestoneValue *int, maxRedemptions *int) string {
	t.Helper()
	ctx := context.Background()
	var id string
	now := time.Now()
	startDate := now.Add(-24 * time.Hour)
	endDate := now.Add(24 * time.Hour)

	err := db.DB.QueryRow(ctx, `
		INSERT INTO discount_campaigns (name, campaign_type, discount_percent, status, start_date, end_date, milestone_type, milestone_value, milestone_unit, max_redemptions, times_redeemed)
		VALUES ($1, $2, $3, 'active', $4, $5, $6, $7, $8, $9, 0)
		RETURNING id
	`, name, campaignType, percent, startDate, endDate, milestoneType, milestoneValue, milestoneUnit, maxRedemptions).Scan(&id)
	require.NoError(t, err)
	return id
}

func createCompletedBooking(t *testing.T, userID, serviceID string, startTime time.Time, price float64) string {
	t.Helper()
	ctx := context.Background()
	var bookingID string
	err := db.DB.QueryRow(ctx, `
		INSERT INTO bookings (user_id, start_time, status)
		VALUES ($1, $2, 'completed')
		RETURNING id
	`, userID, startTime).Scan(&bookingID)
	require.NoError(t, err)

	_, err = db.DB.Exec(ctx, `
		INSERT INTO booking_services (booking_id, service_id, override_price)
		VALUES ($1, $2, $3)
	`, bookingID, serviceID, price)
	require.NoError(t, err)

	return bookingID
}

func createPendingBooking(t *testing.T, userID, serviceID string, startTime time.Time) string {
	t.Helper()
	ctx := context.Background()
	var bookingID string
	err := db.DB.QueryRow(ctx, `
		INSERT INTO bookings (user_id, start_time, status)
		VALUES ($1, $2, 'confirmed')
		RETURNING id
	`, userID, startTime).Scan(&bookingID)
	require.NoError(t, err)

	_, err = db.DB.Exec(ctx, `
		INSERT INTO booking_services (booking_id, service_id)
		VALUES ($1, $2)
	`, bookingID, serviceID)
	require.NoError(t, err)

	return bookingID
}

func completeBooking(t *testing.T, bookingID string) *httptest.ResponseRecorder {
	t.Helper()
	progressReq := ProgressBookingRequest{Status: "completed"}
	handler := http.HandlerFunc(ProgressBookingHandler)
	w := makeProgressRequest(handler, "PUT", "/api/admin/bookings/"+bookingID+"/progress", progressReq, "admin-token")
	require.Equal(t, http.StatusOK, w.Code, "Expected 200 on booking completion")
	return w
}

func backdateBooking(t *testing.T, bookingID string, daysAgo int) {
	t.Helper()
	if daysAgo > 0 {
		_, err := db.DB.Exec(context.Background(), `
			UPDATE bookings SET updated_at = NOW() - INTERVAL '1 day' * $1 WHERE id = $2
		`, daysAgo, bookingID)
		require.NoError(t, err)
	}
}

func getStamps(t *testing.T, userID string) int {
	t.Helper()
	var stamps int
	err := db.DB.QueryRow(context.Background(), `SELECT loyalty_stamps FROM users WHERE id = $1`, userID).Scan(&stamps)
	require.NoError(t, err)
	return stamps
}

func getPendingRedemptions(t *testing.T, userID string) int {
	t.Helper()
	var count int
	err := db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM loyalty_redemptions WHERE user_id = $1 AND status = 'pending'`, userID).Scan(&count)
	require.NoError(t, err)
	return count
}

func getDiscountForBooking(t *testing.T, bookingID string) (source string, amount float64, exists bool) {
	t.Helper()
	err := db.DB.QueryRow(context.Background(), `
		SELECT discount_source, discount_amount FROM booking_discounts WHERE booking_id = $1
	`, bookingID).Scan(&source, &amount)
	if err != nil {
		return "", 0, false
	}
	return source, amount, true
}

// =============================================================================
// Loyalty Auto-Redemption Tests
// =============================================================================

func TestDiscount_Loyalty_FullCycle(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 0)
	serviceID := createTestService(t, 50.00)

	// Simulate completing 10 bookings on different days by directly setting stamps
	_, err := db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
	require.NoError(t, err)

	// Verify pending redemption was created when stamps hit 10
	_, err = db.DB.Exec(context.Background(), `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	assert.Equal(t, 10, getStamps(t, userID))
	assert.Equal(t, 1, getPendingRedemptions(t, userID))

	// 11th booking → applies discount, resets stamps to 0, then +1 for completion
	bookingID11 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 12))
	completeBooking(t, bookingID11)

	source, amount, exists := getDiscountForBooking(t, bookingID11)
	require.True(t, exists, "Expected discount on 11th booking")
	assert.Equal(t, "loyalty", source)
	assert.Equal(t, 5.00, amount, "10% of £50 = £5")
	assert.Equal(t, 1, getStamps(t, userID), "Stamps: 0 after redemption + 1 for this booking")
	assert.Equal(t, 0, getPendingRedemptions(t, userID), "No pending redemptions after applying")
}

func TestDiscount_Loyalty_ExistingRedemptionApplies(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 10)
	serviceID := createTestService(t, 100.00)

	_, err := db.DB.Exec(context.Background(), `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	source, amount, exists := getDiscountForBooking(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "loyalty", source)
	assert.Equal(t, 10.00, amount, "10% of £100 = £10")
	assert.Equal(t, 1, getStamps(t, userID), "Stamp earned for completing this booking (0+1)")

	var redemptionStatus string
	err = db.DB.QueryRow(context.Background(), `
		SELECT status FROM loyalty_redemptions WHERE user_id = $1 AND applied_to_booking_id = $2
	`, userID, bookingID).Scan(&redemptionStatus)
	require.NoError(t, err)
	assert.Equal(t, "applied", redemptionStatus)
}

func TestDiscount_Loyalty_OneStampPerDay(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 5)
	serviceID := createTestService(t, 50.00)

	// Complete 3 bookings on the same day
	var sameDayBookings []string
	for i := 0; i < 3; i++ {
		bookingID := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, i+1))
		completeBooking(t, bookingID)
		sameDayBookings = append(sameDayBookings, bookingID)
	}
	// Backdate all to same past day
	for _, bid := range sameDayBookings {
		backdateBooking(t, bid, 5)
	}

	stamps := getStamps(t, userID)
	assert.Equal(t, 6, stamps, "Only 1 stamp added for same-day completions (5+1=6)")
	assert.Equal(t, 0, getPendingRedemptions(t, userID), "No pending redemption yet")

	// Complete a booking on a different day → second stamp
	bookingID := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 4))
	completeBooking(t, bookingID)

	stamps = getStamps(t, userID)
	assert.Equal(t, 7, stamps, "Second stamp added on different day (6+1=7)")
}

func TestDiscount_Loyalty_ZeroTotalNoStamp(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 5)

	var serviceID string
	err := db.DB.QueryRow(context.Background(), `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, "Free Service", "A free service", 0.00, 60, true, 16).Scan(&serviceID)
	require.NoError(t, err)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	stamps := getStamps(t, userID)
	assert.Equal(t, 5, stamps, "Zero-total booking should not earn a stamp")

	_, _, exists := getDiscountForBooking(t, bookingID)
	assert.False(t, exists, "Zero-total booking should not get any discount")
}

func TestDiscount_Loyalty_CycleRepeats(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 0)
	serviceID := createTestService(t, 50.00)

	// Simulate first cycle
	_, err := db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
	require.NoError(t, err)
	_, err = db.DB.Exec(context.Background(), `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	// 11th booking → discount, stamps reset
	bookingID11 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 12))
	completeBooking(t, bookingID11)

	source, _, exists := getDiscountForBooking(t, bookingID11)
	require.True(t, exists)
	assert.Equal(t, "loyalty", source)
	assert.Equal(t, 0, getPendingRedemptions(t, userID), "Redemption consumed")

	// Simulate second cycle
	_, err = db.DB.Exec(context.Background(), `UPDATE users SET loyalty_stamps = 10 WHERE id = $1`, userID)
	require.NoError(t, err)
	_, err = db.DB.Exec(context.Background(), `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	// 22nd booking → second discount
	bookingID22 := createPendingBooking(t, userID, serviceID, time.Now().AddDate(0, 0, 31))
	completeBooking(t, bookingID22)

	source2, amount2, exists2 := getDiscountForBooking(t, bookingID22)
	require.True(t, exists2)
	assert.Equal(t, "loyalty", source2)
	assert.Equal(t, 5.00, amount2)
	assert.Equal(t, 0, getPendingRedemptions(t, userID), "Second redemption also consumed")
}

// =============================================================================
// Time-Based Campaign Tests
// =============================================================================

func TestDiscount_TimeBasedCampaign(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	campaignID := createTestCampaign(t, "Summer Sale", "time_based", 5.0, nil, nil, nil, nil)

	userID := createTestUser(t, 0)
	serviceID := createTestService(t, 100.00)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	source, campaignType, exists := getDiscountSourceAndType(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "campaign", source)
	assert.Equal(t, "time_based", campaignType)

	var timesRedeemed int
	err := db.DB.QueryRow(context.Background(), `SELECT times_redeemed FROM discount_campaigns WHERE id = $1`, campaignID).Scan(&timesRedeemed)
	require.NoError(t, err)
	assert.Equal(t, 1, timesRedeemed)

	var paymentAmount float64
	err = db.DB.QueryRow(context.Background(), `
		SELECT amount FROM payments WHERE booking_id = $1 AND payment_method = 'discount'
	`, bookingID).Scan(&paymentAmount)
	require.NoError(t, err)
	assert.Equal(t, 5.00, paymentAmount)
}

func getDiscountSourceAndType(t *testing.T, bookingID string) (source, campaignType string, exists bool) {
	t.Helper()
	err := db.DB.QueryRow(context.Background(), `
		SELECT discount_source, campaign_type FROM booking_discounts WHERE booking_id = $1
	`, bookingID).Scan(&source, &campaignType)
	if err != nil {
		return "", "", false
	}
	return source, campaignType, true
}

// =============================================================================
// Per-User Milestone Tests
// =============================================================================

func TestDiscount_PerUserMilestone(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	milestoneValue := 10
	milestoneType := "per_user_booking_count"
	_ = createTestCampaign(t, "10th Visit Bonus", "milestone", 15.0, &milestoneType, nil, &milestoneValue, nil)

	userID := createTestUser(t, 0)
	serviceID := createTestService(t, 80.00)

	for i := 0; i < 9; i++ {
		startTime := time.Now().AddDate(0, -1, -i*7)
		_ = createCompletedBooking(t, userID, serviceID, startTime, 80.00)
	}

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	source, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "campaign", source)
	assert.Equal(t, "per_user_booking_count", milestoneTypeResult)

	var discountAmount float64
	err := db.DB.QueryRow(context.Background(), `SELECT discount_amount FROM booking_discounts WHERE booking_id = $1`, bookingID).Scan(&discountAmount)
	require.NoError(t, err)
	assert.Equal(t, 12.00, discountAmount, "15% of £80 = £12")

	bookingID2 := createPendingBooking(t, userID, serviceID, time.Now().Add(48*time.Hour))
	completeBooking(t, bookingID2)

	var discountCount int
	err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount)
	require.NoError(t, err)
	assert.Equal(t, 0, discountCount, "No second discount (dedup)")
}

func getDiscountSourceAndMilestone(t *testing.T, bookingID string) (source, milestoneType string, exists bool) {
	t.Helper()
	err := db.DB.QueryRow(context.Background(), `
		SELECT discount_source, milestone_type FROM booking_discounts WHERE booking_id = $1
	`, bookingID).Scan(&source, &milestoneType)
	if err != nil {
		return "", "", false
	}
	return source, milestoneType, true
}

// =============================================================================
// Global Milestone Tests
// =============================================================================

func TestDiscount_GlobalMilestone(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	milestoneValue := 5
	milestoneType := "global_booking_count"
	_ = createTestCampaign(t, "5th Customer Milestone", "milestone", 20.0, &milestoneType, nil, &milestoneValue, nil)

	userID1 := createTestUser(t, 0)
	userID2 := createTestUser(t, 0)
	serviceID := createTestService(t, 50.00)

	for i := 0; i < 4; i++ {
		startTime := time.Now().AddDate(0, 0, -i-1)
		_ = createCompletedBooking(t, userID1, serviceID, startTime, 50.00)
	}

	bookingID := createPendingBooking(t, userID2, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	_, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "global_booking_count", milestoneTypeResult)

	var discountAmount float64
	err := db.DB.QueryRow(context.Background(), `SELECT discount_amount FROM booking_discounts WHERE booking_id = $1`, bookingID).Scan(&discountAmount)
	require.NoError(t, err)
	assert.Equal(t, 10.00, discountAmount, "20% of £50 = £10")
}

// =============================================================================
// Anniversary Milestone Tests
// =============================================================================

func TestDiscount_AnniversaryMilestone(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	milestoneValue := 6
	milestoneType := "anniversary"
	milestoneUnit := "months"
	_ = createTestCampaign(t, "6 Month Anniversary", "milestone", 10.0, &milestoneType, &milestoneUnit, &milestoneValue, nil)

	userID := createTestUser(t, 0)
	serviceID := createTestService(t, 60.00)

	ctx := context.Background()
	sevenMonthsAgo := time.Now().AddDate(0, -7, 0)
	var firstBookingID string
	err := db.DB.QueryRow(ctx, `
		INSERT INTO bookings (user_id, start_time, status)
		VALUES ($1, $2, 'completed')
		RETURNING id
	`, userID, sevenMonthsAgo).Scan(&firstBookingID)
	require.NoError(t, err)

	_, err = db.DB.Exec(ctx, `
		INSERT INTO booking_services (booking_id, service_id, override_price)
		VALUES ($1, $2, $3)
	`, firstBookingID, serviceID, 60.00)
	require.NoError(t, err)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	_, milestoneTypeResult, exists := getDiscountSourceAndMilestone(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "anniversary", milestoneTypeResult)

	bookingID2 := createPendingBooking(t, userID, serviceID, time.Now().Add(48*time.Hour))
	completeBooking(t, bookingID2)

	var discountCount int
	err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount)
	require.NoError(t, err)
	assert.Equal(t, 0, discountCount, "No second discount (dedup)")
}

// =============================================================================
// Loyalty Priority Tests
// =============================================================================

func TestDiscount_LoyaltyPriority(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	_ = createTestCampaign(t, "Summer Sale", "time_based", 5.0, nil, nil, nil, nil)

	userID := createTestUser(t, 10)

	ctx := context.Background()
	_, err := db.DB.Exec(ctx, `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	serviceID := createTestService(t, 100.00)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	source, _, exists := getDiscountForBooking(t, bookingID)
	require.True(t, exists)
	assert.Equal(t, "loyalty", source)

	var campaignDiscountCount int
	err = db.DB.QueryRow(context.Background(), `
		SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1 AND discount_source = 'campaign'
	`, bookingID).Scan(&campaignDiscountCount)
	require.NoError(t, err)
	assert.Equal(t, 0, campaignDiscountCount, "No campaign discount when loyalty applies")
}

// =============================================================================
// Zero Total Edge Case Tests
// =============================================================================

func TestDiscount_NoDiscountOnZeroTotal(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	userID := createTestUser(t, 10)

	ctx := context.Background()
	_, err := db.DB.Exec(ctx, `
		INSERT INTO loyalty_redemptions (user_id, stamps_redeemed, status, redeemed_at)
		VALUES ($1, 10, 'pending', NOW())
	`, userID)
	require.NoError(t, err)

	var serviceID string
	err = db.DB.QueryRow(ctx, `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, "Free Service", "A free service", 0.00, 60, true, 16).Scan(&serviceID)
	require.NoError(t, err)

	bookingID := createPendingBooking(t, userID, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID)

	var paymentCount int
	err = db.DB.QueryRow(context.Background(), `
		SELECT COUNT(*) FROM payments WHERE booking_id = $1 AND payment_method = 'discount'
	`, bookingID).Scan(&paymentCount)
	require.NoError(t, err)
	assert.Equal(t, 0, paymentCount, "No discount payment (total is 0)")

	var redemptionStatus string
	err = db.DB.QueryRow(context.Background(), `
		SELECT status FROM loyalty_redemptions WHERE user_id = $1 AND status = 'pending'
	`, userID).Scan(&redemptionStatus)
	require.NoError(t, err)
	assert.Equal(t, "pending", redemptionStatus, "Redemption stays pending")

	assert.Equal(t, 10, getStamps(t, userID), "Stamps unchanged")
}

// =============================================================================
// Max Redemptions Tests
// =============================================================================

func TestDiscount_CampaignMaxRedemptions(t *testing.T) {
	resetTestData(t)
	seedDefaultWorkingHours(t)

	maxRedemptions := 1
	campaignID := createTestCampaign(t, "Limited Time Offer", "time_based", 10.0, nil, nil, nil, &maxRedemptions)

	userID1 := createTestUser(t, 0)
	userID2 := createTestUser(t, 0)
	serviceID := createTestService(t, 50.00)

	bookingID1 := createPendingBooking(t, userID1, serviceID, time.Now().Add(24*time.Hour))
	completeBooking(t, bookingID1)

	var discountCount1 int
	err := db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID1).Scan(&discountCount1)
	require.NoError(t, err)
	assert.Equal(t, 1, discountCount1)

	var timesRedeemed int
	err = db.DB.QueryRow(context.Background(), `SELECT times_redeemed FROM discount_campaigns WHERE id = $1`, campaignID).Scan(&timesRedeemed)
	require.NoError(t, err)
	assert.Equal(t, 1, timesRedeemed)

	bookingID2 := createPendingBooking(t, userID2, serviceID, time.Now().Add(48*time.Hour))
	completeBooking(t, bookingID2)

	var discountCount2 int
	err = db.DB.QueryRow(context.Background(), `SELECT COUNT(*) FROM booking_discounts WHERE booking_id = $1`, bookingID2).Scan(&discountCount2)
	require.NoError(t, err)
	assert.Equal(t, 0, discountCount2, "No discount (max reached)")
}
