//go:build test && dev

package payments

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

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

	"github.com/go-chi/chi/v5"
	"github.com/jackc/pgx/v5"
)

func TestAdminCreateGiftCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{"amount": 50.00})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards", CreateGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Errorf("expected status 201, got %d", w.Code)
	}

	var gc GiftCard
	if err := json.NewDecoder(w.Body).Decode(&gc); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if gc.TotalFundsAdded != 50.00 || gc.AmountRemaining != 50.00 {
		t.Errorf("expected funds and remaining to be 50.00, got added=%.2f remaining=%.2f", gc.TotalFundsAdded, gc.AmountRemaining)
	}
}

func TestAdminTopUpGiftCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create gift card
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by)
		VALUES (50.00, 50.00, $1)
		RETURNING id
	`, adminID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to insert gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         25.00,
		"payment_method": "on_the_house",
	})
	req := httptest.NewRequest("PUT", "/api/admin/gift-cards/"+cardID+"/topup", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Put("/api/admin/gift-cards/{id}/topup", TopUpGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var gc GiftCard
	if err := json.NewDecoder(w.Body).Decode(&gc); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if gc.TotalFundsAdded != 75.00 || gc.AmountRemaining != 75.00 {
		t.Errorf("expected topped up card totals, got added=%.2f remaining=%.2f", gc.TotalFundsAdded, gc.AmountRemaining)
	}
}

func TestAdminTransferGiftCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create card 1 with £100
	var card1ID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by)
		VALUES (100.00, 100.00, $1)
		RETURNING id
	`, adminID).Scan(&card1ID)
	if err != nil {
		t.Fatalf("failed to insert card 1: %v", err)
	}

	// Create card 2 with £20
	var card2ID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by)
		VALUES (20.00, 20.00, $1)
		RETURNING id
	`, adminID).Scan(&card2ID)
	if err != nil {
		t.Fatalf("failed to insert card 2: %v", err)
	}

	// Transfer £30 from card 1 to card 2
	reqBody, _ := json.Marshal(map[string]interface{}{
		"to_card_id": card2ID,
		"amount":     30.00,
	})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards/"+card1ID+"/transfer", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards/{from}/transfer", TransferGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	// Verify card 1 has £70 remaining
	var card1Remaining float64
	err = tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", card1ID).Scan(&card1Remaining)
	if err != nil {
		t.Fatalf("failed to query card 1: %v", err)
	}
	if card1Remaining != 70.00 {
		t.Errorf("expected card 1 to have 70.00, got %.2f", card1Remaining)
	}

	// Verify card 2 has £50 remaining and £50 total funds added
	var card2Remaining, card2Added float64
	err = tx.QueryRow(ctx, "SELECT amount_remaining, total_funds_added FROM gift_cards WHERE id = $1", card2ID).Scan(&card2Remaining, &card2Added)
	if err != nil {
		t.Fatalf("failed to query card 2: %v", err)
	}
	if card2Remaining != 50.00 || card2Added != 50.00 {
		t.Errorf("expected card 2 to have remaining=50.00 added=50.00, got remaining=%.2f added=%.2f", card2Remaining, card2Added)
	}
}

func TestUserRedeemGiftCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	token := jwt.GenerateTestToken(userID, "verified_email")

	// Create gift card with £100
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining)
		VALUES (100.00, 100.00)
		RETURNING id
	`).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to insert gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{"code": cardID})
	req := httptest.NewRequest("POST", "/api/user/giftcards/redeem", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/user/giftcards/redeem", RedeemGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	// Verify card marked as spent (remaining = 0) and claimed
	var amountRemaining float64
	var redeemedBy string
	err = tx.QueryRow(ctx, "SELECT amount_remaining, redeemed_by FROM gift_cards WHERE id = $1", cardID).Scan(&amountRemaining, &redeemedBy)
	if err != nil {
		t.Fatalf("failed to query gift card: %v", err)
	}
	if amountRemaining != 0.00 {
		t.Errorf("expected card to be spent, got remaining=%.2f", amountRemaining)
	}
	if redeemedBy != userID {
		t.Errorf("expected card redeemed_by to be user, got '%s'", redeemedBy)
	}

	// Verify balance added to user
	var balance float64
	err = tx.QueryRow(ctx, "SELECT balance FROM user_giftcard_balances WHERE user_id = $1", userID).Scan(&balance)
	if err != nil {
		t.Fatalf("failed to query user balance: %v", err)
	}
	if balance != 100.00 {
		t.Errorf("expected user balance to be 100.00, got %.2f", balance)
	}
}

func TestBuyGiftCard_Self(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	token := jwt.GenerateTestToken(userID, "verified_email")

	// Charge a mock payment token
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":          2000, // £20.00 in cents
		"recipient_type":  "self",
		"new_card_token":  "cnon:card-nonce-ok",
		"idempotency_key": "idempotency-key-buy-gc-self",
	})
	req := httptest.NewRequest("POST", "/api/user/giftcards/buy", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/user/giftcards/buy", BuyGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Errorf("expected status 201, got %d. Body: %s", w.Code, w.Body.String())
	}

	// Verify user balance is now £20.00
	var balance float64
	err = tx.QueryRow(ctx, "SELECT balance FROM user_giftcard_balances WHERE user_id = $1", userID).Scan(&balance)
	if err != nil {
		t.Fatalf("failed to query user balance: %v", err)
	}
	if balance != 20.00 {
		t.Errorf("expected user balance 20.00, got %.2f", balance)
	}

	// Verify purchase payment record was created
	var payCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE created_by = $1", userID).Scan(&payCount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if payCount != 1 {
		t.Errorf("expected 1 payment record, got %d", payCount)
	}
}

func TestBuyGiftCard_TransactionFailure_SkipsSquare(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	token := jwt.GenerateTestToken(userID, "verified_email")

	// Create a cancelled context so the nested transaction fails
	cancelCtx, cancel := context.WithCancel(ctx)
	cancel()

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":          2000,
		"recipient_type":  "self",
		"new_card_token":  "cnon:card-nonce-ok",
		"idempotency_key": "idempotency-key-buy-gc-txn-fail",
	})
	req := httptest.NewRequest("POST", "/api/user/giftcards/buy", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(cancelCtx, tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/user/giftcards/buy", BuyGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusInternalServerError {
		t.Errorf("expected status 500 due to cancelled context, got %d. Body: %s", w.Code, w.Body.String())
	}

	// Verify no completed payment records exist — confirming Square was never called
	var payCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE created_by = $1 AND status = 'completed'", userID).Scan(&payCount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if payCount != 0 {
		t.Errorf("expected 0 completed payment records (Square should not have been called), got %d", payCount)
	}
}

func TestBuyGiftCard_Friend(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	token := jwt.GenerateTestToken(userID, "verified_email")

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":          5000, // £50.00 in cents
		"recipient_type":  "friend",
		"new_card_token":  "cnon:card-nonce-ok",
		"idempotency_key": "idempotency-key-buy-gc-friend",
	})
	req := httptest.NewRequest("POST", "/api/user/giftcards/buy", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/user/giftcards/buy", BuyGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Errorf("expected status 201, got %d. Body: %s", w.Code, w.Body.String())
	}

	var resp map[string]interface{}
	json.NewDecoder(w.Body).Decode(&resp)
	cardID := resp["code"].(string)

	// Verify card was created with £50.00 remaining (stays active, unredeemed)
	var remaining, added float64
	var redeemedBy sql.NullString
	err = tx.QueryRow(ctx, "SELECT amount_remaining, total_funds_added, redeemed_by FROM gift_cards WHERE id = $1", cardID).Scan(&remaining, &added, &redeemedBy)
	if err != nil {
		t.Fatalf("failed to query card: %v", err)
	}
	if remaining != 50.00 || added != 50.00 {
		t.Errorf("expected card values to be 50.00, got remaining=%.2f added=%.2f", remaining, added)
	}
	if redeemedBy.Valid {
		t.Errorf("expected card redeemed_by to be null, got '%s'", redeemedBy.String)
	}
}

func TestAdminRecordPayment_CashAndGiftCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	serviceID, err := fixtures.CreateTestService(tx)
	if err != nil {
		t.Fatalf("failed to create service: %v", err)
	}

	bookingID, err := fixtures.CreateTestBooking(tx, adminID, serviceID)
	if err != nil {
		t.Fatalf("failed to create booking: %v", err)
	}

	// Update booking to in_progress so it is payable
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// Create a physical gift card code with £100 balance
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining)
		VALUES (100.00, 100.00)
		RETURNING id
	`).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to create gift card: %v", err)
	}

	// 1. Pay £30 with CASH
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000, // £30.00 in cents
		"payment_type":   "full",
		"payment_method": "cash",
	})
	req := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()

	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/bookings/{id}/payment", CreateTerminalPayment)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	// Verify cash payment recorded
	var cashPayCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE booking_id = $1 AND payment_method = 'cash'", bookingID).Scan(&cashPayCount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if cashPayCount != 1 {
		t.Errorf("expected 1 cash payment, got %d", cashPayCount)
	}

	// 2. Pay £40 with PHYSICAL GIFT CARD (guest checkout simulation)
	reqBody2, _ := json.Marshal(map[string]interface{}{
		"amount":         4000, // £40.00 in cents
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody2))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(db.ContextWithTx(req2.Context(), tx.(pgx.Tx)))

	w2 := httptest.NewRecorder()
	r2 := chi.NewRouter()
	r2.Use(mw.RequireAuth)
	r2.Post("/api/admin/bookings/{id}/payment", CreateTerminalPayment)
	r2.ServeHTTP(w2, req2)

	if w2.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w2.Code, w2.Body.String())
	}

	// Verify gift card balance deducted from card directly
	var remaining float64
	err = tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", cardID).Scan(&remaining)
	if err != nil {
		t.Fatalf("failed to query card: %v", err)
	}
	if remaining != 60.00 {
		t.Errorf("expected gift card balance to be 60.00, got %.2f", remaining)
	}

	// Verify gift card payment record created
	var gcPayCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'", bookingID).Scan(&gcPayCount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if gcPayCount != 1 {
		t.Errorf("expected 1 gift card payment, got %d", gcPayCount)
	}

	// 3. Redeem remaining £60 of gift card to user account
	// Setup user account with some balance first
	_, _ = tx.Exec(ctx, "INSERT INTO user_giftcard_balances (user_id, balance) VALUES ($1, 60.00)", adminID)

	// Now pay £25 using user account balance
	reqBody3, _ := json.Marshal(map[string]interface{}{
		"amount":         2500, // £25.00 in cents
		"payment_type":   "full",
		"payment_method": "giftcard",
	})
	req3 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody3))
	req3.Header.Set("Authorization", "Bearer "+token)
	req3.Header.Set("Content-Type", "application/json")
	req3 = req3.WithContext(db.ContextWithTx(req3.Context(), tx.(pgx.Tx)))

	w3 := httptest.NewRecorder()
	r3 := chi.NewRouter()
	r3.Use(mw.RequireAuth)
	r3.Post("/api/admin/bookings/{id}/payment", CreateTerminalPayment)
	r3.ServeHTTP(w3, req3)

	if w3.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d. Body: %s", w3.Code, w3.Body.String())
	}

	// Verify user account balance was deducted
	var userBalance float64
	err = tx.QueryRow(ctx, "SELECT balance FROM user_giftcard_balances WHERE user_id = $1", adminID).Scan(&userBalance)
	if err != nil {
		t.Fatalf("failed to query user balance: %v", err)
	}
	if userBalance != 35.00 {
		t.Errorf("expected user balance to be 35.00, got %.2f", userBalance)
	}
}

// --- New Tests for branch features ---

func TestGetExpiredBalances(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Seed expired balances
	for i := 0; i < 2; i++ {
		_, err = tx.Exec(ctx, `
			INSERT INTO gift_card_expired_balances (account_id, original_balance, expired_at)
			VALUES ($1, $2, NOW() - interval '30 days')
		`, adminID, float64(25.00*(i+1)))
		if err != nil {
			t.Fatalf("failed to seed expired balance %d: %v", i, err)
		}
	}

	req := httptest.NewRequest("GET", "/api/admin/gift-cards/expired-balances", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Get("/api/admin/gift-cards/expired-balances", GetExpiredBalances)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected 200, got %d", w.Code)
	}

	var resp map[string]interface{}
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	total, ok := resp["total"].(float64)
	if !ok || total != 2 {
		t.Errorf("expected total 2, got %v", resp["total"])
	}

	balances, ok := resp["expired_balances"].([]interface{})
	if !ok || len(balances) != 2 {
		t.Errorf("expected 2 expired_balances, got %d", len(balances))
	}
}

func TestClaimExpiredBalance_HappyPath(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Seed an expired balance with known ID
	var balanceID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_card_expired_balances (account_id, original_balance, expired_at)
		VALUES ($1, 50.00, NOW() - interval '30 days')
		RETURNING id
	`, adminID).Scan(&balanceID)
	if err != nil {
		t.Fatalf("failed to seed expired balance: %v", err)
	}

	notes := "claimed via test"
	reqBody, _ := json.Marshal(map[string]interface{}{
		"balance_id": balanceID,
		"notes":      notes,
	})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards/expired-balances/claim", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards/expired-balances/claim", ClaimExpiredBalance)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var resp map[string]string
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}
	if resp["status"] != "claimed" {
		t.Errorf("expected status 'claimed', got '%s'", resp["status"])
	}

	// Verify claimed_at is set in DB
	var claimedAt sql.NullTime
	err = tx.QueryRow(ctx, "SELECT claimed_at FROM gift_card_expired_balances WHERE id = $1", balanceID).Scan(&claimedAt)
	if err != nil {
		t.Fatalf("failed to query expired balance: %v", err)
	}
	if !claimedAt.Valid {
		t.Error("expected claimed_at to be set, got null")
	}
}

func TestClaimExpiredBalance_AlreadyClaimed(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Seed an expired balance that is already claimed
	var balanceID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_card_expired_balances (account_id, original_balance, expired_at, claimed_at, claimed_by_admin)
		VALUES ($1, 50.00, NOW() - interval '30 days', NOW(), $2)
		RETURNING id
	`, adminID, adminID).Scan(&balanceID)
	if err != nil {
		t.Fatalf("failed to seed claimed expired balance: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"balance_id": balanceID,
	})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards/expired-balances/claim", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards/expired-balances/claim", ClaimExpiredBalance)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusConflict {
		t.Errorf("expected 409, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestClaimExpiredBalance_NotFound(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{
		"balance_id": "aaaaaaaaaaaa",
	})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards/expired-balances/claim", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards/expired-balances/claim", ClaimExpiredBalance)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusNotFound {
		t.Errorf("expected 404, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestGetGiftCards_Pagination(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create 15 gift cards
	for i := 0; i < 15; i++ {
		_, err = tx.Exec(ctx, `
			INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by)
			VALUES (10.00, 10.00, $1)
		`, adminID)
		if err != nil {
			t.Fatalf("failed to create gift card %d: %v", i, err)
		}
	}

	// Request page 1 with per_page=5
	req := httptest.NewRequest("GET", "/api/admin/gift-cards?page=1&per_page=5", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Get("/api/admin/gift-cards", GetGiftCards)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var resp GiftCardListResponse
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if len(resp.GiftCards) != 5 {
		t.Errorf("expected 5 gift cards on page 1, got %d", len(resp.GiftCards))
	}
	if resp.Total != 15 {
		t.Errorf("expected total 15, got %d", resp.Total)
	}
	if resp.Page != 1 {
		t.Errorf("expected page 1, got %d", resp.Page)
	}
	if resp.PerPage != 5 {
		t.Errorf("expected perPage 5, got %d", resp.PerPage)
	}
	if resp.TotalPages != 3 {
		t.Errorf("expected totalPages 3, got %d", resp.TotalPages)
	}

	// Request page 3 to verify last page
	req3 := httptest.NewRequest("GET", "/api/admin/gift-cards?page=3&per_page=5", nil)
	req3.Header.Set("Authorization", "Bearer "+token)
	req3 = req3.WithContext(db.ContextWithTx(req3.Context(), tx.(pgx.Tx)))

	w3 := httptest.NewRecorder()
	r3 := chi.NewRouter()
	r3.Use(mw.RequireAuth)
	r3.Get("/api/admin/gift-cards", GetGiftCards)
	r3.ServeHTTP(w3, req3)

	if w3.Code != http.StatusOK {
		t.Errorf("expected 200, got %d", w3.Code)
	}

	var resp3 GiftCardListResponse
	if err := json.NewDecoder(w3.Body).Decode(&resp3); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if len(resp3.GiftCards) != 5 {
		t.Errorf("expected 5 gift cards on page 3, got %d", len(resp3.GiftCards))
	}
	if resp3.Total != 15 {
		t.Errorf("expected total 15 on page 3, got %d", resp3.Total)
	}
}

func TestGetGiftCards_Search(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create cards with specific hex IDs for search predictability
	searchableID := "aaaaaabbbbcc"
	nonSearchableID := "ddddeeeeffff"

	_, err = tx.Exec(ctx, `
		INSERT INTO gift_cards (id, total_funds_added, amount_remaining, created_by)
		VALUES ($1, 10.00, 10.00, $2)
	`, searchableID, adminID)
	if err != nil {
		t.Fatalf("failed to create searchable card: %v", err)
	}

	_, err = tx.Exec(ctx, `
		INSERT INTO gift_cards (id, total_funds_added, amount_remaining, created_by)
		VALUES ($1, 20.00, 20.00, $2)
	`, nonSearchableID, adminID)
	if err != nil {
		t.Fatalf("failed to create non-searchable card: %v", err)
	}

	// Search by partial ID match
	req := httptest.NewRequest("GET", "/api/admin/gift-cards?q=aaaa", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Get("/api/admin/gift-cards", GetGiftCards)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var resp GiftCardListResponse
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if resp.Total != 1 {
		t.Errorf("expected total 1, got %d", resp.Total)
	}
	if len(resp.GiftCards) != 1 {
		t.Errorf("expected 1 gift card in results, got %d", len(resp.GiftCards))
	}
	if len(resp.GiftCards) > 0 && resp.GiftCards[0].ID != searchableID {
		t.Errorf("expected card ID %s, got %s", searchableID, resp.GiftCards[0].ID)
	}
}

func TestCreateGiftCard_NegativeAmount(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{"amount": -10.00})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards", CreateGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("expected 400 for negative amount, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestCreateGiftCard_ZeroAmountNoInventory(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{"amount": 0, "is_inventory": false})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards", CreateGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("expected 400 for zero amount without inventory, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestCreateGiftCard_ZeroAmountInventory(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{"amount": 0, "is_inventory": true})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards", CreateGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Errorf("expected 201 for inventory card with zero amount, got %d. Body: %s", w.Code, w.Body.String())
	}

	var gc GiftCard
	if err := json.NewDecoder(w.Body).Decode(&gc); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	if !gc.IsInventory {
		t.Error("expected card to be inventory card")
	}
	if gc.TotalFundsAdded != 0 || gc.AmountRemaining != 0 {
		t.Errorf("expected zero balance card, got added=%.2f remaining=%.2f", gc.TotalFundsAdded, gc.AmountRemaining)
	}
}

func TestTopUpGiftCard_InvalidPaymentMethod(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create gift card
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by)
		VALUES (50.00, 50.00, $1)
		RETURNING id
	`, adminID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to insert gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         25.00,
		"payment_method": "invalid_method",
	})
	req := httptest.NewRequest("PUT", "/api/admin/gift-cards/"+cardID+"/topup", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Put("/api/admin/gift-cards/{id}/topup", TopUpGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("expected 400 for invalid payment method, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestTopUpGiftCard_RedeemedCard(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Create a card that's already redeemed
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, redeemed_at, redeemed_by)
		VALUES (50.00, 0, $1, NOW(), $1)
		RETURNING id
	`, adminID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to insert redeemed gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         25.00,
		"payment_method": "on_the_house",
	})
	req := httptest.NewRequest("PUT", "/api/admin/gift-cards/"+cardID+"/topup", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Put("/api/admin/gift-cards/{id}/topup", TopUpGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusBadRequest {
		t.Errorf("expected 400 for redeemed card topup, got %d. Body: %s", w.Code, w.Body.String())
	}
}

func TestBuyGiftCard_Idempotency(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	token := jwt.GenerateTestToken(userID, "verified_email")

	idempotencyKey := "idempotent-buy-gc-test"

	reqBody := map[string]interface{}{
		"amount":          2000,
		"recipient_type":  "self",
		"new_card_token":  "cnon:card-nonce-ok",
		"idempotency_key": idempotencyKey,
	}

	// Send request
	body1, _ := json.Marshal(reqBody)
	req1 := httptest.NewRequest("POST", "/api/user/giftcards/buy", bytes.NewBuffer(body1))
	req1.Header.Set("Authorization", "Bearer "+token)
	req1.Header.Set("Content-Type", "application/json")
	req1 = req1.WithContext(db.ContextWithTx(req1.Context(), tx.(pgx.Tx)))

	w1 := httptest.NewRecorder()
	r1 := chi.NewRouter()
	r1.Use(mw.RequireAuth)
	r1.Post("/api/user/giftcards/buy", BuyGiftCard)
	r1.ServeHTTP(w1, req1)

	if w1.Code != http.StatusCreated && w1.Code != http.StatusOK {
		t.Errorf("buy request: expected 201 or 200, got %d. Body: %s", w1.Code, w1.Body.String())
	}

	// Verify exactly one payment record was created for this key
	var payCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE idempotency_key = $1", idempotencyKey).Scan(&payCount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if payCount != 1 {
		t.Errorf("expected 1 payment record for idempotency key, got %d", payCount)
	}

	// Verify exactly one user balance record
	var balCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances WHERE user_id = $1", userID).Scan(&balCount)
	if err != nil {
		t.Fatalf("failed to query user balances: %v", err)
	}
	if balCount != 1 {
		t.Errorf("expected 1 user balance record, got %d", balCount)
	}

	// Verify exactly one gift card was created for self-purchase (amount_remaining=0, redeemed)
	var cardCount int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards WHERE redeemed_by = $1", userID).Scan(&cardCount)
	if err != nil {
		t.Fatalf("failed to query gift cards: %v", err)
	}
	if cardCount != 1 {
		t.Errorf("expected 1 gift card record, got %d", cardCount)
	}

	// Send second request with same idempotency key
	body2, _ := json.Marshal(reqBody)
	req2 := httptest.NewRequest("POST", "/api/user/giftcards/buy", bytes.NewBuffer(body2))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(db.ContextWithTx(req2.Context(), tx.(pgx.Tx)))

	w2 := httptest.NewRecorder()
	r2 := chi.NewRouter()
	r2.Use(mw.RequireAuth)
	r2.Post("/api/user/giftcards/buy", BuyGiftCard)
	r2.ServeHTTP(w2, req2)

	// Verify counts remain unchanged (idempotent)
	var payCount2 int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM payments WHERE idempotency_key = $1", idempotencyKey).Scan(&payCount2)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	if payCount2 != 1 {
		t.Errorf("after second request: expected 1 payment record, got %d", payCount2)
	}

	var balCount2 int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM user_giftcard_balances WHERE user_id = $1", userID).Scan(&balCount2)
	if err != nil {
		t.Fatalf("failed to query user balances: %v", err)
	}
	if balCount2 != 1 {
		t.Errorf("after second request: expected 1 user balance record, got %d", balCount2)
	}

	var cardCount2 int
	err = tx.QueryRow(ctx, "SELECT COUNT(*) FROM gift_cards WHERE redeemed_by = $1", userID).Scan(&cardCount2)
	if err != nil {
		t.Fatalf("failed to query gift cards: %v", err)
	}
	if cardCount2 != 1 {
		t.Errorf("after second request: expected 1 gift card record, got %d", cardCount2)
	}
}

// TestAdminCreateGiftCard_ExpiryDateIsNull verifies that gift cards created via
// CreateGiftCard no longer have expiry_date set (rolling 24-month expiry via last_used_at).
func TestAdminCreateGiftCard_ExpiryDateIsNull(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	reqBody, _ := json.Marshal(map[string]interface{}{"amount": 50.00})
	req := httptest.NewRequest("POST", "/api/admin/gift-cards", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/gift-cards", CreateGiftCard)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("expected 201, got %d", w.Code)
	}

	var gc GiftCard
	if err := json.NewDecoder(w.Body).Decode(&gc); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	// Verify expiry_date is NULL in the database
	var expiryDate *time.Time
	err = tx.QueryRow(ctx, `SELECT expiry_date FROM gift_cards WHERE id = $1`, gc.ID).Scan(&expiryDate)
	if err != nil {
		t.Fatalf("failed to query gift card expiry_date: %v", err)
	}
	if expiryDate != nil {
		t.Error("expected expiry_date to be NULL for rolling-expiry gift cards")
	}
}

// TestGetGiftCards_InventoryFilter verifies the ?type=customer|inventory query parameter.
func TestGetGiftCards_InventoryFilter(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	token := jwt.GenerateTestToken(adminID, "admin")

	// Insert one customer card and one inventory card
	_, err = tx.Exec(ctx, `INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory) VALUES (10.00, 10.00, $1, FALSE)`, adminID)
	if err != nil {
		t.Fatalf("failed to create customer card: %v", err)
	}
	_, err = tx.Exec(ctx, `INSERT INTO gift_cards (total_funds_added, amount_remaining, created_by, is_inventory) VALUES (20.00, 20.00, $1, TRUE)`, adminID)
	if err != nil {
		t.Fatalf("failed to create inventory card: %v", err)
	}

	// Test ?type=customer filter
	reqCustomer := httptest.NewRequest("GET", "/api/admin/gift-cards?type=customer", nil)
	reqCustomer.Header.Set("Authorization", "Bearer "+token)
	reqCustomer = reqCustomer.WithContext(db.ContextWithTx(reqCustomer.Context(), tx.(pgx.Tx)))

	wCustomer := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Get("/api/admin/gift-cards", GetGiftCards)
	r.ServeHTTP(wCustomer, reqCustomer)

	if wCustomer.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d", wCustomer.Code)
	}

	var respCustomer GiftCardListResponse
	if err := json.NewDecoder(wCustomer.Body).Decode(&respCustomer); err != nil {
		t.Fatalf("failed to decode customer response: %v", err)
	}

	customerCount := 0
	inventoryCount := 0
	for _, gc := range respCustomer.GiftCards {
		if gc.IsInventory {
			inventoryCount++
		} else {
			customerCount++
		}
	}

	if respCustomer.Total != 1 {
		t.Errorf("expected total 1 (customer cards only), got %d", respCustomer.Total)
	}
	if customerCount != 1 {
		t.Errorf("expected 1 customer card in filtered list, got %d", customerCount)
	}
	if inventoryCount != 0 {
		t.Errorf("expected 0 inventory cards in customer filter, got %d", inventoryCount)
	}

	// Test ?type=inventory filter
	reqInventory := httptest.NewRequest("GET", "/api/admin/gift-cards?type=inventory", nil)
	reqInventory.Header.Set("Authorization", "Bearer "+token)
	reqInventory = reqInventory.WithContext(db.ContextWithTx(reqInventory.Context(), tx.(pgx.Tx)))

	wInventory := httptest.NewRecorder()
	rInventory := chi.NewRouter()
	rInventory.Use(mw.RequireAuth)
	rInventory.Get("/api/admin/gift-cards", GetGiftCards)
	rInventory.ServeHTTP(wInventory, reqInventory)

	if wInventory.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d", wInventory.Code)
	}

	var respInventory GiftCardListResponse
	if err := json.NewDecoder(wInventory.Body).Decode(&respInventory); err != nil {
		t.Fatalf("failed to decode inventory response: %v", err)
	}

	if respInventory.Total != 1 {
		t.Errorf("expected total 1 (inventory cards only), got %d", respInventory.Total)
	}
	for _, gc := range respInventory.GiftCards {
		if !gc.IsInventory {
			t.Errorf("expected only inventory cards, got customer card %s", gc.ID)
		}
	}

	// Test no filter (should return both)
	reqAll := httptest.NewRequest("GET", "/api/admin/gift-cards", nil)
	reqAll.Header.Set("Authorization", "Bearer "+token)
	reqAll = reqAll.WithContext(db.ContextWithTx(reqAll.Context(), tx.(pgx.Tx)))

	wAll := httptest.NewRecorder()
	rAll := chi.NewRouter()
	rAll.Use(mw.RequireAuth)
	rAll.Get("/api/admin/gift-cards", GetGiftCards)
	rAll.ServeHTTP(wAll, reqAll)

	if wAll.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d", wAll.Code)
	}

	var respAll GiftCardListResponse
	if err := json.NewDecoder(wAll.Body).Decode(&respAll); err != nil {
		t.Fatalf("failed to decode all response: %v", err)
	}
	if respAll.Total != 2 {
		t.Errorf("expected total 2 (all cards), got %d", respAll.Total)
	}
}

// TestGetUserGiftCardBalanceAdmin_AuditLog verifies admin balance checks
// are recorded in the admin_audit_log table.
func TestGetUserGiftCardBalanceAdmin_AuditLog(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE users SET account_role = 'admin' WHERE id = $1", adminID)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create test user: %v", err)
	}

	// Give the user a balance
	_, err = tx.Exec(ctx, `INSERT INTO user_giftcard_balances (user_id, balance) VALUES ($1, 42.50)`, userID)
	if err != nil {
		t.Fatalf("failed to insert user balance: %v", err)
	}

	token := jwt.GenerateTestToken(adminID, "admin")

	// Set up request with chi route context for URL param extraction
	req := httptest.NewRequest("GET", "/"+userID+"/giftcard-balance", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	req = req.WithContext(db.ContextWithTx(req.Context(), tx.(pgx.Tx)))

	rctx := chi.NewRouteContext()
	rctx.URLParams.Add("id", userID)
	ctxWithRoute := context.WithValue(req.Context(), chi.RouteCtxKey, rctx)
	req = req.WithContext(ctxWithRoute)

	// Add user context (from token)
	info := extractUserFromTestJWT(token)
	if info != nil {
		reqCtx := context.WithValue(req.Context(), mw.UserIDKey, info.userID)
		reqCtx = context.WithValue(reqCtx, mw.UserRoleKey, info.role)
		req = req.WithContext(reqCtx)
	}

	w := httptest.NewRecorder()
	GetUserGiftCardBalanceAdmin(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("expected 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var resp map[string]float64
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}
	if resp["balance"] != 42.50 {
		t.Errorf("expected balance 42.50, got %.2f", resp["balance"])
	}

	// Verify audit log entry was created
	var logCount int
	err = tx.QueryRow(ctx, `SELECT COUNT(*) FROM admin_audit_log WHERE admin_id = $1 AND target_user_id = $2 AND action_type = 'balance_check'`, adminID, userID).Scan(&logCount)
	if err != nil {
		t.Fatalf("failed to query admin_audit_log: %v", err)
	}
	if logCount != 1 {
		t.Errorf("expected 1 audit log entry, got %d", logCount)
	}
}
