//go:build test && dev

package payments

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

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

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

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

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

	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        50.00,
		PaymentMethod: "cash",
	}

	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for SPV till sale")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set for SPV till sale")
	}
	if vatAmount.Float64 != 8.33 {
		t.Errorf("expected vat_amount 8.33, got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set for SPV till sale")
	}
	if netAmount.Float64 != 41.67 {
		t.Errorf("expected net_amount 41.67, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

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

	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        50.00,
		PaymentMethod: "cash",
	}

	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for MPV till sale")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for MPV till sale, got %.2f", vatAmount.Float64)
	}
	if netAmount.Valid {
		t.Errorf("expected net_amount to be NULL for MPV till sale, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	var cardID string
	var vtp string
	tx.QueryRow(ctx, `SELECT COALESCE(voucher_type, 'SPV') FROM business_settings LIMIT 1`).Scan(&vtp)
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, voucher_type_at_purchase)
		VALUES (100.00, 100.00, $1)
		RETURNING id
	`, vtp).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to create gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	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(ctx)

	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.Fatalf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for MPV giftcard payment")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set for MPV giftcard payment")
	}
	if vatAmount.Float64 != 8.33 {
		t.Errorf("expected vat_amount 8.33, got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set for MPV giftcard payment")
	}
	if netAmount.Float64 != 41.67 {
		t.Errorf("expected net_amount 41.67, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining, voucher_type_at_purchase)
		VALUES (100.00, 100.00, 'SPV')
		RETURNING id
	`).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to create gift card: %v", err)
	}

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	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(ctx)

	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.Fatalf("expected status 200, got %d. Body: %s", w.Code, w.Body.String())
	}

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for SPV giftcard payment")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for SPV giftcard payment, got %.2f", vatAmount.Float64)
	}
	if netAmount.Valid {
		t.Errorf("expected net_amount to be NULL for SPV giftcard payment, got %.2f", netAmount.Float64)
	}
}
func TestVAT_SkippedWhenNotRegistered(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

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

	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        50.00,
		PaymentMethod: "cash",
	}

	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE when not VAT registered")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL when not VAT registered, got %.2f", vatAmount.Float64)
	}
	if netAmount.Valid {
		t.Errorf("expected net_amount to be NULL when not VAT registered, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	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)
	}

	var paymentID string
	err = tx.QueryRow(ctx, `
		INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, is_vat_applicable, vat_amount, net_amount, created_by, created_at, updated_at)
		VALUES ($1, 'full', 'cash', 'completed', 100.00, FALSE, NULL, NULL, $2, NOW(), NOW())
		RETURNING id
	`, bookingID, adminID).Scan(&paymentID)
	if err != nil {
		t.Fatalf("failed to insert payment: %v", err)
	}

	var initialVAT sql.NullFloat64
	err = tx.QueryRow(ctx, "SELECT vat_amount FROM payments WHERE id = $1", paymentID).Scan(&initialVAT)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}
	if initialVAT.Valid {
		t.Fatal("expected vat_amount to be NULL before enable_vat_registration")
	}

	var paymentsUpdated, tillSalesUpdated int
	err = tx.QueryRow(ctx, "SELECT payments_updated, till_sales_updated FROM enable_vat_registration(CURRENT_DATE, 20.00, 'GB123456789')").Scan(&paymentsUpdated, &tillSalesUpdated)
	if err != nil {
		t.Fatalf("enable_vat_registration failed: %v", err)
	}
	if paymentsUpdated < 1 {
		t.Errorf("expected at least 1 payment updated, got %d", paymentsUpdated)
	}

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, "SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1", paymentID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE after enable_vat_registration")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set after enable_vat_registration")
	}
	if vatAmount.Float64 != 16.67 {
		t.Errorf("expected vat_amount 16.67 (100/1.2*0.2), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set after enable_vat_registration")
	}
	if netAmount.Float64 != 83.33 {
		t.Errorf("expected net_amount 83.33 (100/1.2), got %.2f", netAmount.Float64)
	}

	var isRegistered bool
	var regNumber *string
	err = tx.QueryRow(ctx, "SELECT is_vat_registered, vat_registration_number FROM business_settings WHERE id = 1").Scan(&isRegistered, &regNumber)
	if err != nil {
		t.Fatalf("failed to query business_settings: %v", err)
	}
	if !isRegistered {
		t.Error("expected is_vat_registered to be TRUE after enable_vat_registration")
	}
	if regNumber == nil || *regNumber != "GB123456789" {
		t.Errorf("expected vat_registration_number 'GB123456789', got %v", regNumber)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// £30 cash payment — with 20% VAT: net=25.00, vat=5.00
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"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(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash'`, bookingID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for cash payment when VAT registered")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set for cash payment")
	}
	if vatAmount.Float64 != 5.00 {
		t.Errorf("expected vat_amount 5.00 (£30 at 20%%), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set for cash payment")
	}
	if netAmount.Float64 != 25.00 {
		t.Errorf("expected net_amount 25.00, got %.2f", netAmount.Float64)
	}
}

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

	// Explicitly not registered (default)
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"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(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash'`, bookingID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE when not VAT registered")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL when not registered, got %.2f", vatAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin user: %v", err)
	}
	adminToken := jwt.GenerateTestToken(adminID, "admin")

	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        50.00,
		PaymentMethod: "on_the_house",
	}

	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for on_the_house")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for on_the_house, got %.2f", vatAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 5.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	cfg, err := GetVATConfig(ctx, db.Conn)
	if err != nil {
		t.Fatalf("GetVATConfig failed: %v", err)
	}

	if !cfg.IsVATRegistered {
		t.Error("expected IsVATRegistered to be TRUE")
	}
	if cfg.DefaultVATRate != 5.00 {
		t.Errorf("expected DefaultVATRate 5.00, got %.2f", cfg.DefaultVATRate)
	}
	if cfg.VoucherType != "MPV" {
		t.Errorf("expected VoucherType MPV, got %s", cfg.VoucherType)
	}

	// Test with FALSE
	_, _ = tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE`)
	cfg2, err := GetVATConfig(ctx, db.Conn)
	if err != nil {
		t.Fatalf("GetVATConfig failed: %v", err)
	}
	if cfg2.IsVATRegistered {
		t.Error("expected IsVATRegistered to be FALSE after update")
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}
	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)
	}

	// Insert a payment WITHOUT VAT first
	var paymentID string
	err = tx.QueryRow(ctx, `
		INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, is_vat_applicable, created_by, created_at, updated_at)
		VALUES ($1, 'full', 'cash', 'completed', 60.00, FALSE, $2, NOW(), NOW())
		RETURNING id
	`, bookingID, adminID).Scan(&paymentID)
	if err != nil {
		t.Fatalf("failed to insert payment: %v", err)
	}

	// Apply VAT via the helper — uses db.Conn which routes through transaction context
	ApplyVATToBookingPayment(ctx, db.Conn, paymentID)

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1`, paymentID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE after ApplyVATToBookingPayment")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set")
	}
	if vatAmount.Float64 != 10.00 {
		t.Errorf("expected vat_amount 10.00 (£60 at 20%%), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 50.00 {
		t.Errorf("expected net_amount 50.00, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Insert a till_sale WITHOUT VAT first
	var saleID string
	err = tx.QueryRow(ctx, `
		INSERT INTO till_sales (item_type, item_id, description, quantity, unit_price, total_amount, payment_method, status, created_by, created_at, updated_at)
		VALUES ('gift_card', NULL, 'test', 1, 75.00, 75.00, 'cash', 'completed', $1, NOW(), NOW())
		RETURNING id
	`, adminID).Scan(&saleID)
	if err != nil {
		t.Fatalf("failed to insert till_sale: %v", err)
	}

	ApplyVATToTillSale(ctx, db.Conn, saleID)

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, saleID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sale: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set")
	}
	if vatAmount.Float64 != 12.50 {
		t.Errorf("expected vat_amount 12.50 (£75 at 20%%), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 62.50 {
		t.Errorf("expected net_amount 62.50, got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Create till sale via handler (first application of VAT)
	adminToken := jwt.GenerateTestToken(adminID, "admin")
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	// Read values after first apply
	var vat1 sql.NullFloat64
	var net1 sql.NullFloat64
	err = tx.QueryRow(ctx, `SELECT vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&vat1, &net1)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	// Apply VAT a second time — should be idempotent (AND vat_amount IS NULL guard)
	ApplyVATToTillSale(ctx, db.Conn, resp.ID)

	var vat2 sql.NullFloat64
	var net2 sql.NullFloat64
	err = tx.QueryRow(ctx, `SELECT vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&vat2, &net2)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if vat2.Float64 != vat1.Float64 {
		t.Errorf("vat_amount changed on second apply: before=%.2f after=%.2f", vat1.Float64, vat2.Float64)
	}
	if net2.Float64 != net1.Float64 {
		t.Errorf("net_amount changed on second apply: before=%.2f after=%.2f", net1.Float64, net2.Float64)
	}
}

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

	// Test with 5% reduced VAT rate
	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 5.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Create a till sale — should use 5% rate
	adminToken := jwt.GenerateTestToken(adminID, "admin")
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

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

	var vatAmount, netAmount float64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE")
	}
	// £100 at 5%: net = 100/1.05 = 95.24, vat = 100 - 95.24 = 4.76
	if vatAmount != 4.76 {
		t.Errorf("expected vat_amount 4.76 (£100 at 5%%), got %.2f", vatAmount)
	}
	if netAmount != 95.24 {
		t.Errorf("expected net_amount 95.24, got %.2f", netAmount)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 90.00 WHERE id = $1", bookingID)

	// Pay £30 cash — VAT applied via handler
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	req := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody))
	req.Header.Set("Authorization", "Bearer "+jwt.GenerateTestToken(adminID, "admin"))
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Pay another £60 cash
	reqBody2, _ := json.Marshal(map[string]interface{}{
		"amount":         6000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody2))
	req2.Header.Set("Authorization", "Bearer "+jwt.GenerateTestToken(adminID, "admin"))
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(ctx)

	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.Fatalf("expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	// Now check PaymentSummary aggregates
	svc := NewPaymentService()
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("GetBookingPaymentSummary failed: %v", err)
	}

	if summary.TotalAmount != 90.00 {
		t.Errorf("expected TotalAmount 90.00, got %.2f", summary.TotalAmount)
	}
	if summary.PaidAmount != 90.00 {
		t.Errorf("expected PaidAmount 90.00, got %.2f", summary.PaidAmount)
	}

	// £30 payment: net=25.00, vat=5.00
	// £60 payment: net=50.00, vat=10.00
	// Total vat: 15.00, Total net: 75.00
	if summary.TotalVATAmount != 15.00 {
		t.Errorf("expected TotalVATAmount 15.00, got %.2f", summary.TotalVATAmount)
	}
	if summary.TotalNetAmount != 75.00 {
		t.Errorf("expected TotalNetAmount 75.00, got %.2f", summary.TotalNetAmount)
	}

	if summary.RemainingAmount != 0.00 {
		t.Errorf("expected RemainingAmount 0.00, got %.2f", summary.RemainingAmount)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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":          2000,
		"recipient_type":  "self",
		"new_card_token":  "cnon:card-nonce-ok",
		"idempotency_key": "idempotency-buy-gc-vat-test",
	})
	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(ctx)

	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.Fatalf("expected 201, got %d: %s", w.Code, w.Body.String())
	}

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	var paymentAmount float64
	err = tx.QueryRow(ctx, `
		SELECT p.amount, p.is_vat_applicable, p.vat_amount, p.net_amount
		FROM payments p
		WHERE p.created_by = $1
		ORDER BY p.created_at DESC LIMIT 1
	`, userID).Scan(&paymentAmount, &isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if paymentAmount != 20.00 {
		t.Errorf("expected amount 20.00, got %.2f", paymentAmount)
	}
	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for SPV gift card purchase")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set")
	}
	if vatAmount.Float64 != 3.33 {
		t.Errorf("expected vat_amount 3.33 (£20 at 20%%), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 16.67 {
		t.Errorf("expected net_amount 16.67, got %.2f", netAmount.Float64)
	}

	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 balance: %v", err)
	}
	if balance != 20.00 {
		t.Errorf("expected balance 20.00, got %.2f", balance)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to set initial state: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 200.00 WHERE id = $1", bookingID)

	// Phase 1: Not VAT registered — pay £50 cash
	reqBody1, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	req1 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(reqBody1))
	req1.Header.Set("Authorization", "Bearer "+token)
	req1.Header.Set("Content-Type", "application/json")
	req1 = req1.WithContext(ctx)

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

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

	// Verify no VAT on phase 1 payment
	var phase1VAT sql.NullFloat64
	var phase1VATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' AND amount = 50.00`, bookingID).Scan(&phase1VATApplicable, &phase1VAT)
	if err != nil {
		t.Fatalf("phase 1: failed to query payment: %v", err)
	}
	if phase1VATApplicable {
		t.Error("phase 1: expected no VAT when not registered")
	}
	if phase1VAT.Valid {
		t.Errorf("phase 1: expected NULL vat_amount, got %.2f", phase1VAT.Float64)
	}

	// Phase 2: Enable VAT registration
	_, err = tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE`)
	if err != nil {
		t.Fatalf("phase 2: failed to enable VAT: %v", err)
	}

	// Phase 3: Pay another £50 cash — should have VAT
	reqBody3, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("phase 3: expected 200, got %d: %s", w3.Code, w3.Body.String())
	}

	var phase3Resp CheckoutResponse
	if err := json.Unmarshal(w3.Body.Bytes(), &phase3Resp); err != nil {
		t.Fatalf("phase 3: failed to parse response: %v", err)
	}

	var phase3VAT sql.NullFloat64
	var phase3Net sql.NullFloat64
	var phase3VATApplicable bool
	var phase3Amount float64
	// Get the specific payment by its ID from the response (avoiding
	// non-deterministic ORDER BY created_at DESC when NOW() returns the same
	// transaction start time for all inserts within a transaction).
	err = tx.QueryRow(ctx, `SELECT amount, is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1`, phase3Resp.CheckoutID).Scan(&phase3Amount, &phase3VATApplicable, &phase3VAT, &phase3Net)
	if err != nil {
		t.Fatalf("phase 3: failed to query payment: %v", err)
	}

	if !phase3VATApplicable {
		t.Error("phase 3: expected VAT to be applied after registration")
	}
	if !phase3VAT.Valid {
		t.Fatal("phase 3: expected vat_amount to be set")
	}
	if phase3VAT.Float64 != 8.33 {
		t.Errorf("phase 3: expected vat_amount 8.33, got %.2f", phase3VAT.Float64)
	}
	if !phase3Net.Valid {
		t.Fatal("phase 3: expected net_amount to be set")
	}
	if phase3Net.Float64 != 41.67 {
		t.Errorf("phase 3: expected net_amount 41.67, got %.2f", phase3Net.Float64)
	}

	// Phase 4: Verify PaymentSummary reflects both payments
	svc := NewPaymentService()
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("phase 4: GetBookingPaymentSummary failed: %v", err)
	}
	if summary.PaidAmount != 100.00 {
		t.Errorf("phase 4: expected PaidAmount 100.00, got %.2f", summary.PaidAmount)
	}
	if summary.TotalVATAmount != 8.33 {
		t.Errorf("phase 4: expected TotalVATAmount 8.33 (only 2nd payment has VAT), got %.2f", summary.TotalVATAmount)
	}
	if summary.TotalNetAmount != 91.67 {
		t.Errorf("phase 4: expected TotalNetAmount 91.67 (50 + 41.67), got %.2f", summary.TotalNetAmount)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

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

	bookingID, err := fixtures.CreateTestBookingAtTime(tx, userID, serviceID,
		time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC))
	if err != nil {
		t.Fatalf("failed to create booking: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// Create a £50 cash payment with VAT via the handler
	token := jwt.GenerateTestToken(adminID, "admin")
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"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(ctx)

	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.Fatalf("payment handler: expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Verify payment has VAT
	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash'`, bookingID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}
	if !isVATApplicable {
		t.Fatal("expected payment to have VAT before refund test")
	}

	// Now cancel the booking (>72h before = full refund)
	now := time.Date(2099, 12, 28, 8, 0, 0, 0, time.UTC)
	start := time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC)

	result, pErr := ProcessCancellationRefund(ctx, bookingID, 100, 50, start, now, "client_cancelled", &userID)
	if pErr != nil {
		t.Fatalf("ProcessCancellationRefund failed: %v", pErr)
	}
	if result == nil {
		t.Fatal("expected non-nil result")
	}
	if result.RefundableAmount != 50 {
		t.Errorf("expected refundable 50, got %.2f", result.RefundableAmount)
	}

	// Verify refund record created — amount should be the gross £50
	var refundAmount float64
	err = tx.QueryRow(ctx, `SELECT COALESCE(SUM(amount), 0) FROM refunds WHERE booking_id = $1`, bookingID).Scan(&refundAmount)
	if err != nil {
		t.Fatalf("failed to query refunds: %v", err)
	}
	if refundAmount != 50.00 {
		t.Errorf("expected refund amount 50.00 (gross, including VAT), got %.2f", refundAmount)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Phase 1: Buy gift card via till sale (cash) — SPV, so VAT at sale
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("till sale: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	// Verify till_sale has VAT applied
	var tsVAT sql.NullFloat64
	var tsNet sql.NullFloat64
	var tsVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&tsVATApplicable, &tsVAT, &tsNet)
	if !tsVATApplicable {
		t.Error("SPV: expected till_sale VAT applicable")
	}
	if !tsVAT.Valid || tsVAT.Float64 != 16.67 {
		t.Errorf("SPV: expected till_sale vat 16.67, got %.2f", tsVAT.Float64)
	}

	// Get the gift card ID from the till sale
	var cardID string
	err = tx.QueryRow(ctx, "SELECT item_id FROM till_sales WHERE id = $1", tsResp.ID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to get gift card ID: %v", err)
	}

	// Phase 2: Create a booking and redeem the gift card
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// Redeem £50 via gift card
	redeemBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(redeemBody))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(ctx)

	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.Fatalf("redemption: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	// Verify NO VAT on redemption (SPV — VAT already at sale)
	var payVAT sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT)
	if payVATApplicable {
		t.Error("SPV redemption: expected NO VAT at redemption")
	}
	if payVAT.Valid {
		t.Errorf("SPV redemption: expected NULL vat, got %.2f", payVAT.Float64)
	}

	// Verify gift card balance
	var remaining float64
	tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", cardID).Scan(&remaining)
	if remaining != 50.00 {
		t.Errorf("expected remaining balance 50.00, got %.2f", remaining)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Phase 1: Buy gift card via till sale (cash) — MPV, so NO VAT at sale
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("till sale: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	// Verify till_sale has NO VAT (MPV)
	var tsVAT sql.NullFloat64
	var tsVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&tsVATApplicable, &tsVAT)
	if tsVATApplicable {
		t.Error("MPV: expected NO till_sale VAT")
	}
	if tsVAT.Valid {
		t.Errorf("MPV: expected NULL vat_amount at sale, got %.2f", tsVAT.Float64)
	}

	// Get the gift card ID
	var cardID string
	err = tx.QueryRow(ctx, "SELECT item_id FROM till_sales WHERE id = $1", tsResp.ID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to get gift card ID: %v", err)
	}

	// Phase 2: Create a booking and redeem the gift card — MPV so VAT at redemption
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	redeemBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(redeemBody))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(ctx)

	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.Fatalf("redemption: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	// Verify VAT IS applied at redemption (MPV)
	var payVAT sql.NullFloat64
	var payNet sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT, &payNet)
	if !payVATApplicable {
		t.Error("MPV redemption: expected VAT at redemption")
	}
	if !payVAT.Valid {
		t.Fatal("MPV redemption: expected vat_amount to be set")
	}
	if payVAT.Float64 != 8.33 {
		t.Errorf("MPV redemption: expected vat 8.33, got %.2f", payVAT.Float64)
	}
	if !payNet.Valid {
		t.Fatal("MPV redemption: expected net_amount to be set")
	}
	if payNet.Float64 != 41.67 {
		t.Errorf("MPV redemption: expected net 41.67, got %.2f", payNet.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	token := jwt.GenerateTestToken(adminID, "admin")

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

	// Top up via till sale (cash) — should apply VAT (SPV)
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "topup",
		GiftCardID:    &cardID,
		Amount:        25.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("topup: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	// Verify till_sale has VAT applied
	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&isVATApplicable, &vatAmount)
	if !isVATApplicable {
		t.Error("expected VAT on topup till sale (SPV)")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount on topup")
	}
	// £25 at 20%: vat = 25 - (25/1.2) = 25 - 20.83 = 4.17
	if vatAmount.Float64 != 4.17 {
		t.Errorf("expected vat_amount 4.17, got %.2f", vatAmount.Float64)
	}

	// Verify gift card balance increased
	var remaining float64
	tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", cardID).Scan(&remaining)
	if remaining != 75.00 {
		t.Errorf("expected remaining 75.00, got %.2f", remaining)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 100.00 WHERE id = $1", bookingID)

	// Pay £30 cash with VAT
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"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(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Remaining balance should be total - paid (gross) = 100 - 30 = 70
	svc := NewPaymentService()
	remaining, rErr := svc.GetBookingRemainingBalanceCents(ctx, bookingID)
	if rErr != nil {
		t.Fatalf("GetBookingRemainingBalanceCents failed: %v", rErr)
	}
	if remaining != 7000 {
		t.Errorf("expected remaining 7000 cents (£70), got %d", remaining)
	}

	// Pay another £40 with VAT — remaining should be 100 - 70 = 30
	reqBody2, _ := json.Marshal(map[string]interface{}{
		"amount":         4000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("second payment: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	remaining2, rErr2 := svc.GetBookingRemainingBalanceCents(ctx, bookingID)
	if rErr2 != nil {
		t.Fatalf("GetBookingRemainingBalanceCents failed: %v", rErr2)
	}
	if remaining2 != 3000 {
		t.Errorf("expected remaining 3000 cents (£30), got %d", remaining2)
	}

	// Pay the final £30 — remaining should be 0
	reqBody3, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("third payment: expected 200, got %d: %s", w3.Code, w3.Body.String())
	}

	remaining3, rErr3 := svc.GetBookingRemainingBalanceCents(ctx, bookingID)
	if rErr3 != nil {
		t.Fatalf("GetBookingRemainingBalanceCents failed: %v", rErr3)
	}
	if remaining3 != 0 {
		t.Errorf("expected remaining 0, got %d", remaining3)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	adminToken := jwt.GenerateTestToken(adminID, "admin")

	// Create two gift cards in DB
	var card1ID, card2ID 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 create card 1: %v", err)
	}
	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(&card2ID)
	if err != nil {
		t.Fatalf("failed to create card 2: %v", err)
	}

	// 1) Transfer between cards (card1→card2) — unaffected by VAT
	transferBody, _ := json.Marshal(map[string]interface{}{
		"to_card_id": card2ID,
		"amount":     30.00,
	})
	req1 := httptest.NewRequest("POST", "/api/admin/gift-cards/"+card1ID+"/transfer", bytes.NewBuffer(transferBody))
	req1.Header.Set("Authorization", "Bearer "+adminToken)
	req1.Header.Set("Content-Type", "application/json")
	req1 = req1.WithContext(ctx)
	w1 := httptest.NewRecorder()
	r1 := chi.NewRouter()
	r1.Use(mw.RequireAuth)
	r1.Post("/api/admin/gift-cards/{from}/transfer", TransferGiftCard)
	r1.ServeHTTP(w1, req1)
	if w1.Code != http.StatusOK {
		t.Fatalf("transfer: expected 200, got %d: %s", w1.Code, w1.Body.String())
	}

	// Verify card balances after transfer
	var c1Bal, c2Bal float64
	tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", card1ID).Scan(&c1Bal)
	tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", card2ID).Scan(&c2Bal)
	if c1Bal != 70.00 || c2Bal != 80.00 {
		t.Errorf("expected card1=70.00 card2=80.00, got card1=%.2f card2=%.2f", c1Bal, c2Bal)
	}

	// 2) Get gift cards list — should work with VAT fields present on payments
	getReq := httptest.NewRequest("GET", "/api/admin/giftcards?limit=10&offset=0", nil)
	getReq.Header.Set("Authorization", "Bearer "+adminToken)
	getReq = getReq.WithContext(ctx)
	w2 := httptest.NewRecorder()
	r2 := chi.NewRouter()
	r2.Use(mw.RequireAuth)
	r2.Get("/api/admin/giftcards", GetGiftCards)
	r2.ServeHTTP(w2, getReq)
	if w2.Code != http.StatusOK {
		t.Fatalf("list: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	// 3) Get individual gift card — should work
	getOne := httptest.NewRequest("GET", "/api/admin/giftcards/"+card1ID, nil)
	getOne.Header.Set("Authorization", "Bearer "+adminToken)
	getOne = getOne.WithContext(ctx)
	w3 := httptest.NewRecorder()
	r3 := chi.NewRouter()
	r3.Use(mw.RequireAuth)
	r3.Get("/api/admin/giftcards/{id}", GetGiftCards)
	r3.ServeHTTP(w3, getOne)
	if w3.Code != http.StatusOK {
		t.Fatalf("get one: expected 200, got %d: %s", w3.Code, w3.Body.String())
	}
}

// ─── Additional tests ──────────────────────────────────────────────────────────

// TestMPV_Topup_NoVAT verifies a gift card topup with voucher_type=MPV does not
// apply VAT at sale.
func TestMPV_Topup_NoVAT(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	token := jwt.GenerateTestToken(adminID, "admin")

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

	// Top up via till sale (cash) — MPV, so no VAT
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "topup",
		GiftCardID:    &cardID,
		Amount:        25.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("topup: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	// Verify NO VAT on MPV topup
	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&isVATApplicable, &vatAmount)
	if isVATApplicable {
		t.Error("expected no VAT on MPV topup")
	}
	if vatAmount.Valid {
		t.Errorf("expected NULL vat_amount on MPV topup, got %.2f", vatAmount.Float64)
	}

	// Verify gift card balance still increased
	var remaining float64
	tx.QueryRow(ctx, "SELECT amount_remaining FROM gift_cards WHERE id = $1", cardID).Scan(&remaining)
	if remaining != 75.00 {
		t.Errorf("expected remaining 75.00, got %.2f", remaining)
	}
}

// TestCreateBookingPayment_VATApplied verifies that the online Square payment
// path (CreateBookingPayment) applies VAT when the business is registered.
// Uses a booking that has already started so buildSplitRecords returns a
// single payment record, making VAT assertion straightforward.
func TestCreateBookingPayment_VATApplied(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}
	serviceID, err := fixtures.CreateTestService(tx)
	if err != nil {
		t.Fatalf("failed to create service: %v", err)
	}
	// Use a past start time so buildSplitRecords returns a single record
	bookingID, err := fixtures.CreateTestBookingAtTime(tx, userID, serviceID,
		time.Date(2020, 1, 1, 10, 0, 0, 0, time.UTC))
	if err != nil {
		t.Fatalf("failed to create booking: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	userToken := jwt.GenerateUserToken(userID)

	cardToken := "cnon:vat-online-card"
	req := CreateBookingPaymentRequest{
		Amount:         3000,
		PaymentType:    "full",
		NewCardToken:   &cardToken,
		IdempotencyKey: "booking-vat-online-" + bookingID,
	}

	handler := CreateBookingPayment
	w := makePaymentRequest(handler, "POST", "/api/bookings/"+bookingID+"/payment", req, userToken, ctx)

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

	// Verify VAT applied to the single payment record
	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'online_square'`, bookingID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for online payment when VAT registered")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set")
	}
	// £30 at 20%: net = 30/1.2 = 25.00, vat = 30 - 25 = 5.00
	if vatAmount.Float64 != 5.00 {
		t.Errorf("expected vat_amount 5.00, got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 25.00 {
		t.Errorf("expected net_amount 25.00, got %.2f", netAmount.Float64)
	}
}

// TestTillSale_SavedCard_SPV_VATApplied verifies that a till sale with
// saved_card payment method applies VAT for SPV.
func TestTillSale_SavedCard_SPV_VATApplied(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}
	adminToken := jwt.GenerateTestToken(adminID, "admin")

	// Create a saved card for the admin
	cardID, err := fixtures.CreateTestPaymentMethod(tx, adminID, "ccof:saved-card-test", "VISA", "1111")
	if err != nil {
		t.Fatalf("failed to create saved card: %v", err)
	}

	reqBody := TillSaleRequest{
		ItemType:        "gift_card",
		Action:          "create",
		Amount:          75.00,
		PaymentMethod:   "saved_card",
		UserSavedCardID: &cardID,
		UserID:          &adminID,
	}

	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+adminToken)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

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

	var resp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&resp)

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1`, resp.ID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sales: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE for SPV saved_card till sale")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set")
	}
	// £75 at 20%: vat = 75 - (75/1.2) = 75 - 62.50 = 12.50
	if vatAmount.Float64 != 12.50 {
		t.Errorf("expected vat_amount 12.50, got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 62.50 {
		t.Errorf("expected net_amount 62.50, got %.2f", netAmount.Float64)
	}
}

// TestVoucherToggle_SPVPurchase_MPVRedeem verifies that a gift card bought
// as SPV (VAT at sale) and then redeemed after switching to MPV does NOT
// get double-taxed. The voucher_type_at_purchase stored on the card at
// creation time is used at redemption, not the current business_settings.
func TestVoucherToggle_SPVPurchase_MPVRedeem(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Phase 1: Buy gift card as SPV — VAT at sale
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("till sale: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	// Verify VAT applied at sale (SPV) and voucher_type_at_purchase stored
	var tsVAT sql.NullFloat64
	var tsVATApplicable bool
	var storedVTP sql.NullString
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&tsVATApplicable, &tsVAT)
	if !tsVATApplicable {
		t.Error("SPV purchase: expected VAT at sale")
	}

	var cardID string
	err = tx.QueryRow(ctx, "SELECT item_id FROM till_sales WHERE id = $1", tsResp.ID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to get gift card ID: %v", err)
	}
	err = tx.QueryRow(ctx, "SELECT voucher_type_at_purchase FROM gift_cards WHERE id = $1", cardID).Scan(&storedVTP)
	if err != nil {
		t.Fatalf("failed to get voucher_type_at_purchase from gift card: %v", err)
	}
	if !storedVTP.Valid || storedVTP.String != "SPV" {
		t.Errorf("expected voucher_type_at_purchase 'SPV', got %v", storedVTP)
	}

	// Phase 2: Toggle voucher_type to MPV — should NOT affect already-purchased cards
	_, err = tx.Exec(ctx, `UPDATE business_settings SET voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to toggle to MPV: %v", err)
	}

	// Phase 3: Redeem — stored voucher_type is SPV, so NO VAT at redemption
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	redeemBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(redeemBody))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(ctx)

	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.Fatalf("redemption: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	var payVAT sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT)
	if payVATApplicable {
		t.Error("expected NO VAT at redemption (card stored SPV, VAT already paid at sale)")
	}
	if payVAT.Valid {
		t.Errorf("expected NULL vat at SPV-stored redemption, got %.2f", payVAT.Float64)
	}
}

// TestApplyVATToTillSale_MPV verifies ApplyVATToTillSale has no effect when
// voucher_type is MPV.
func TestApplyVATToTillSale_MPV(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}

	var saleID string
	err = tx.QueryRow(ctx, `
		INSERT INTO till_sales (item_type, item_id, description, quantity, unit_price, total_amount, payment_method, status, created_by, created_at, updated_at)
		VALUES ('gift_card', NULL, 'test', 1, 50.00, 50.00, 'cash', 'completed', $1, NOW(), NOW())
		RETURNING id
	`, adminID).Scan(&saleID)
	if err != nil {
		t.Fatalf("failed to insert till_sale: %v", err)
	}

	ApplyVATToTillSale(ctx, db.Conn, saleID)

	var vatAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, saleID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query till_sale: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for MPV")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for MPV, got %.2f", vatAmount.Float64)
	}
}

// ─── Exhaustive voucher_type toggle + legacy tests ─────────────────────────

// TestVoucherToggle_MPVPurchase_SPVRedeem verifies that a gift card bought
// as MPV (no VAT at sale, deferred to redemption) and then redeemed after
// switching to SPV still gets VAT at redemption — because the stored
// voucher_type_at_purchase is MPV, overriding the current business_settings.
func TestVoucherToggle_MPVPurchase_SPVRedeem(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Phase 1: Buy gift card as MPV — no VAT at sale
	reqBody := TillSaleRequest{
		ItemType:      "gift_card",
		Action:        "create",
		Amount:        100.00,
		PaymentMethod: "cash",
	}
	bodyBytes, _ := json.Marshal(reqBody)
	req := httptest.NewRequest("POST", "/api/admin/till/sale", bytes.NewReader(bodyBytes))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	w := httptest.NewRecorder()
	r := chi.NewRouter()
	r.Use(mw.RequireAuth)
	r.Post("/api/admin/till/sale", CreateTillSale)
	r.ServeHTTP(w, req)

	if w.Code != http.StatusCreated {
		t.Fatalf("till sale: expected 201, got %d: %s", w.Code, w.Body.String())
	}
	var tsResp TillSaleResponse
	json.NewDecoder(w.Body).Decode(&tsResp)

	var tsVAT sql.NullFloat64
	var tsVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM till_sales WHERE id = $1`, tsResp.ID).Scan(&tsVATApplicable, &tsVAT)
	if tsVATApplicable {
		t.Error("MPV purchase: expected NO VAT at sale")
	}

	var cardID string
	var storedVTP sql.NullString
	err = tx.QueryRow(ctx, "SELECT item_id FROM till_sales WHERE id = $1", tsResp.ID).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to get card ID: %v", err)
	}
	tx.QueryRow(ctx, "SELECT voucher_type_at_purchase FROM gift_cards WHERE id = $1", cardID).Scan(&storedVTP)
	if !storedVTP.Valid || storedVTP.String != "MPV" {
		t.Errorf("expected voucher_type_at_purchase 'MPV', got %v", storedVTP)
	}

	// Phase 2: Toggle to SPV — should NOT affect already-purchased cards
	_, err = tx.Exec(ctx, `UPDATE business_settings SET voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to toggle to SPV: %v", err)
	}

	// Phase 3: Redeem — stored voucher_type is MPV, so VAT IS applied at redemption
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	redeemBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req2 := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(redeemBody))
	req2.Header.Set("Authorization", "Bearer "+token)
	req2.Header.Set("Content-Type", "application/json")
	req2 = req2.WithContext(ctx)

	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.Fatalf("redemption: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	var payVAT sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT)
	if !payVATApplicable {
		t.Error("expected VAT at redemption (card stored MPV)")
	}
	if !payVAT.Valid {
		t.Fatal("expected vat_amount to be set at MPV-stored redemption")
	}
	if payVAT.Float64 != 8.33 {
		t.Errorf("expected vat 8.33, got %.2f", payVAT.Float64)
	}
}

// TestLegacyGiftCard_NullVoucherType defaults NULL voucher_type_at_purchase
// to SPV behavior — no VAT at redemption.
func TestLegacyGiftCard_NullVoucherType(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// Create a gift card WITHOUT voucher_type_at_purchase (legacy card, NULL)
	var cardID string
	err = tx.QueryRow(ctx, `
		INSERT INTO gift_cards (total_funds_added, amount_remaining)
		VALUES (50.00, 50.00)
		RETURNING id
	`).Scan(&cardID)
	if err != nil {
		t.Fatalf("failed to create legacy gift card: %v", err)
	}

	// Redeem — legacy NULL defaults to SPV, so no VAT at redemption
	redeemBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"payment_type":   "full",
		"payment_method": "giftcard",
		"gift_card_id":   cardID,
	})
	req := httptest.NewRequest("POST", "/api/admin/bookings/"+bookingID+"/payment", bytes.NewBuffer(redeemBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req = req.WithContext(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	var payVAT sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT)
	if payVATApplicable {
		t.Error("expected NO VAT for legacy card (NULL defaults to SPV)")
	}
	if payVAT.Valid {
		t.Errorf("expected NULL vat for legacy card, got %.2f", payVAT.Float64)
	}
}

// TestUsedBalance_NoVATRegardless verifies that when payment comes from a
// user's account balance (usedBalance=true), no VAT is ever applied —
// it was already paid when the card was originally purchased.
func TestUsedBalance_NoVATRegardless(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	// Set MPV — even with MPV, usedBalance should NOT have VAT
	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

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

	// Give the user an account balance (as if they redeemed a gift card)
	_, err = tx.Exec(ctx, `
		INSERT INTO user_giftcard_balances (user_id, balance, updated_at)
		VALUES ($1, 100.00, NOW())
		ON CONFLICT (user_id) DO UPDATE SET balance = 100.00, updated_at = NOW()
	`, userID)
	if err != nil {
		t.Fatalf("failed to set user balance: %v", err)
	}

	serviceID, err := fixtures.CreateTestService(tx)
	if err != nil {
		t.Fatalf("failed to create service: %v", err)
	}
	bookingID, err := fixtures.CreateTestBooking(tx, userID, serviceID)
	if err != nil {
		t.Fatalf("failed to create booking: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	// Pay £50 from account balance (no GiftCardID — uses user balance)
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "giftcard",
	})
	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(ctx)

	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.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
	}

	var payVAT sql.NullFloat64
	var payVATApplicable bool
	tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount FROM payments WHERE booking_id = $1 AND payment_method = 'giftcard'`, bookingID).Scan(&payVATApplicable, &payVAT)
	if payVATApplicable {
		t.Error("expected NO VAT for usedBalance (already paid at purchase)")
	}
	if payVAT.Valid {
		t.Errorf("expected NULL vat for usedBalance, got %.2f", payVAT.Float64)
	}

	// Verify balance was deducted
	var balance float64
	tx.QueryRow(ctx, "SELECT balance FROM user_giftcard_balances WHERE user_id = $1", userID).Scan(&balance)
	if balance != 50.00 {
		t.Errorf("expected balance 50.00 after payment, got %.2f", balance)
	}
}

// ─── VAT + discount interaction ──────────────────────────────────────────────

// TestVAT_DiscountPayment_NoVAT verifies that discount payments never get VAT
// applied, even when the business is VAT-registered.
func TestVAT_DiscountPayment_NoVAT(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 100.00 WHERE id = $1", bookingID)

	// Pay £80 cash first (this creates the completed payment to build records against)
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         8000,
		"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(ctx)

	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.Fatalf("cash payment: expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Now add a discount payment directly (handlers.go doesn't expose a
	// discount endpoint — the till used to handle it, so we insert it via
	// the service layer)
	discountRecord := PaymentRecord{
		BookingID:     bookingID,
		PaymentType:   "full",
		PaymentMethod: "discount",
		Status:        "completed",
		Amount:        10.00,
		CreatedAt:     clock.Now(),
		UpdatedAt:     clock.Now(),
		CreatedBy:     &adminID,
	}
	svc := NewPaymentService()
	discountID, err := svc.CreatePaymentRecord(ctx, discountRecord, nil)
	if err != nil {
		t.Fatalf("failed to create discount payment: %v", err)
	}

	// Apply VAT to the discount payment — should be a no-op
	ApplyVATToBookingPayment(ctx, tx, discountID)

	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1`, discountID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query discount payment: %v", err)
	}

	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for discount payment")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for discount, got %.2f", vatAmount.Float64)
	}

	// Verify PaymentSummary excludes VAT from discount payments
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("GetBookingPaymentSummary failed: %v", err)
	}

	// £80 cash payment: net=66.67, vat=13.33
	// £10 discount: no VAT, net=10.00 (discount amount is the net)
	if summary.TotalVATAmount != 13.33 {
		t.Errorf("expected TotalVATAmount 13.33, got %.2f", summary.TotalVATAmount)
	}
	if summary.TotalNetAmount != 76.67 {
		t.Errorf("expected TotalNetAmount 76.67 (66.67 cash net + 10.00 discount), got %.2f", summary.TotalNetAmount)
	}
	if summary.PaidAmount != 90.00 {
		t.Errorf("expected PaidAmount 90.00 (80 cash + 10 discount), got %.2f", summary.PaidAmount)
	}
}

// ─── VAT + split payments ────────────────────────────────────────────────────

// TestVAT_SplitPayments verifies VAT is correctly partitioned across split
// records when a single Square payment is split into deposit + balance.
func TestVAT_SplitPayments(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	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 := adminID
	serviceID, err := fixtures.CreateTestService(tx)
	if err != nil {
		t.Fatalf("failed to create service: %v", err)
	}
	// Create a booking far in the future (so buildSplitRecords splits into
	// deposit + balance, not a single record)
	bookingID, err := fixtures.CreateTestBookingAtTime(tx, userID, serviceID,
		time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC))
	if err != nil {
		t.Fatalf("failed to create booking: %v", err)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'confirmed' WHERE id = $1", bookingID)

	// Get the booking total
	var totalAmount float64
	err = tx.QueryRow(ctx, "SELECT total_amount FROM bookings WHERE id = $1", bookingID).Scan(&totalAmount)
	if err != nil {
		t.Fatalf("failed to get booking total: %v", err)
	}

	// Process a Square payment via the online handler — this triggers
	// buildSplitRecords which creates split deposit + balance records
	userToken := jwt.GenerateUserToken(userID)
	cardToken := "cnon:split-vat-card"
	req := CreateBookingPaymentRequest{
		Amount:         int64(totalAmount * 100),
		PaymentType:    "full",
		NewCardToken:   &cardToken,
		IdempotencyKey: "split-vat-test-" + bookingID,
	}

	handler := CreateBookingPayment
	w := makePaymentRequest(handler, "POST", "/api/bookings/"+bookingID+"/payment", req, userToken, ctx)

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

	// Query all payment records for this booking
	rows, err := tx.Query(ctx, `
		SELECT payment_type, amount, is_vat_applicable, vat_amount, net_amount
		FROM payments
		WHERE booking_id = $1
		ORDER BY created_at ASC
	`, bookingID)
	if err != nil {
		t.Fatalf("failed to query payments: %v", err)
	}
	defer rows.Close()

	var totalVAT, totalNet, totalAmountPaid float64
	paymentCount := 0
	for rows.Next() {
		var ptype string
		var amount float64
		var isVAT bool
		var vat, net sql.NullFloat64
		if err := rows.Scan(&ptype, &amount, &isVAT, &vat, &net); err != nil {
			t.Fatalf("failed to scan payment: %v", err)
		}
		paymentCount++
		totalAmountPaid += amount
		if isVAT {
			if !vat.Valid {
				t.Errorf("split record %s (%.2f): expected vat_amount to be set", ptype, amount)
			} else {
				totalVAT += vat.Float64
			}
			if !net.Valid {
				t.Errorf("split record %s (%.2f): expected net_amount to be set", ptype, amount)
			} else {
				totalNet += net.Float64
			}
		}
	}

	if paymentCount < 2 {
		t.Errorf("expected at least 2 split payment records, got %d", paymentCount)
	}

	// Total VAT should equal Total - Net across all split records
	if totalNet+totalVAT != totalAmountPaid {
		t.Errorf("net(%.2f) + vat(%.2f) = %.2f, expected %.2f", totalNet, totalVAT, totalNet+totalVAT, totalAmountPaid)
	}

	// Verify each split has proportional VAT
	if totalVAT < 0.01 {
		t.Error("expected non-zero total VAT across split payments")
	}
}

// TestEnableVATRegistration_TillSales verifies that enable_vat_registration
// also retroactively applies VAT to completed till_sales records.
func TestEnableVATRegistration_TillSales(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	adminID, err := fixtures.CreateTestAdminUser(tx)
	if err != nil {
		t.Fatalf("failed to create admin: %v", err)
	}

	// Create a till_sale without VAT (simulating a sale before VAT registration)
	var saleID string
	err = tx.QueryRow(ctx, `
		INSERT INTO till_sales (item_type, item_id, description, quantity, unit_price, total_amount, payment_method, status, created_by, created_at, updated_at)
		VALUES ('gift_card', NULL, 'test', 1, 50.00, 50.00, 'cash', 'completed', $1, NOW(), NOW())
		RETURNING id
	`, adminID).Scan(&saleID)
	if err != nil {
		t.Fatalf("failed to insert till_sale: %v", err)
	}

	// Verify no VAT before
	var initialVAT sql.NullFloat64
	tx.QueryRow(ctx, "SELECT vat_amount FROM till_sales WHERE id = $1", saleID).Scan(&initialVAT)
	if initialVAT.Valid {
		t.Fatal("expected vat_amount to be NULL before enable_vat_registration")
	}

	// Enable VAT retroactively
	var tillSalesUpdated int
	err = tx.QueryRow(ctx, "SELECT till_sales_updated FROM enable_vat_registration(CURRENT_DATE, 20.00, 'GB123456789')").Scan(&tillSalesUpdated)
	if err != nil {
		t.Fatalf("enable_vat_registration failed: %v", err)
	}
	if tillSalesUpdated < 1 {
		t.Errorf("expected at least 1 till_sale updated, got %d", tillSalesUpdated)
	}

	// Verify till_sale now has VAT
	var vatAmount sql.NullFloat64
	var netAmount sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, "SELECT is_vat_applicable, vat_amount, net_amount FROM till_sales WHERE id = $1", saleID).Scan(&isVATApplicable, &vatAmount, &netAmount)
	if err != nil {
		t.Fatalf("failed to query till_sale: %v", err)
	}

	if !isVATApplicable {
		t.Error("expected is_vat_applicable to be TRUE after enable_vat_registration")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount to be set after enable_vat_registration")
	}
	if vatAmount.Float64 != 8.33 {
		t.Errorf("expected vat_amount 8.33 (50/1.2*0.2), got %.2f", vatAmount.Float64)
	}
	if !netAmount.Valid {
		t.Fatal("expected net_amount to be set")
	}
	if netAmount.Float64 != 41.67 {
		t.Errorf("expected net_amount 41.67 (50/1.2), got %.2f", netAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 5.00, voucher_type = 'MPV'`)
	if err != nil {
		t.Fatalf("failed to set VAT config: %v", err)
	}

	// Read via explicit tx querier — must see uncommitted changes.
	cfg, err := GetVATConfig(ctx, tx)
	if err != nil {
		t.Fatalf("GetVATConfig with tx querier failed: %v", err)
	}
	if !cfg.IsVATRegistered {
		t.Error("expected IsVATRegistered to be TRUE (read via tx)")
	}
	if cfg.DefaultVATRate != 5.00 {
		t.Errorf("expected DefaultVATRate 5.00, got %.2f", cfg.DefaultVATRate)
	}
	if cfg.VoucherType != "MPV" {
		t.Errorf("expected VoucherType MPV, got %s", cfg.VoucherType)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 8.00, voucher_type = 'SPV'`)
	if err != nil {
		t.Fatalf("failed to set VAT config: %v", err)
	}

	// Read via db.Conn — in tests, PoolProxy routes through the context tx,
	// so this should also see the uncommitted changes.
	cfg, err := GetVATConfig(ctx, db.Conn)
	if err != nil {
		t.Fatalf("GetVATConfig with db.Conn querier failed: %v", err)
	}
	if !cfg.IsVATRegistered {
		t.Error("expected IsVATRegistered to be TRUE (read via db.Conn)")
	}
	if cfg.DefaultVATRate != 8.00 {
		t.Errorf("expected DefaultVATRate 8.00, got %.2f", cfg.DefaultVATRate)
	}
	if cfg.VoucherType != "SPV" {
		t.Errorf("expected VoucherType SPV, got %s", cfg.VoucherType)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to set VAT config: %v", err)
	}

	// Create a discount payment record directly.
	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}

	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	var paymentID string
	err = tx.QueryRow(ctx, `
		INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, created_by, created_at, updated_at)
		VALUES ($1, 'full', 'discount', 'completed', 10.00, $2, NOW(), NOW())
		RETURNING id
	`, bookingID, adminID).Scan(&paymentID)
	if err != nil {
		t.Fatalf("failed to create discount payment: %v", err)
	}

	// This should skip VAT because payment_method == 'discount'.
	ApplyVATToBookingPayment(ctx, tx, paymentID)

	var isVATApplicable bool
	var vatAmount sql.NullFloat64
	err = tx.QueryRow(ctx, "SELECT is_vat_applicable, vat_amount FROM payments WHERE id = $1", paymentID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}
	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for discount payment (defensive check)")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for discount payment, got %.2f", vatAmount.Float64)
	}
}

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to set VAT config: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %v", err)
	}
	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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

	var paymentID string
	err = tx.QueryRow(ctx, `
		INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, created_by, created_at, updated_at)
		VALUES ($1, 'full', 'on_the_house', 'completed', 25.00, $2, NOW(), NOW())
		RETURNING id
	`, bookingID, adminID).Scan(&paymentID)
	if err != nil {
		t.Fatalf("failed to create on_the_house payment: %v", err)
	}

	// The defensive check reads the payment method inside the same transaction
	// via the tx querier, which must see the just-inserted row.
	ApplyVATToBookingPayment(ctx, tx, paymentID)

	var isVATApplicable bool
	var vatAmount sql.NullFloat64
	err = tx.QueryRow(ctx, "SELECT is_vat_applicable, vat_amount FROM payments WHERE id = $1", paymentID).Scan(&isVATApplicable, &vatAmount)
	if err != nil {
		t.Fatalf("failed to query payment: %v", err)
	}
	if isVATApplicable {
		t.Error("expected is_vat_applicable to be FALSE for on_the_house payment")
	}
	if vatAmount.Valid {
		t.Errorf("expected vat_amount to be NULL for on_the_house, got %.2f", vatAmount.Float64)
	}
}

// ─── VAT disable/enable lifecycle ──────────────────────────────────────────

// TestVAT_DisableVATRegistration_Lifecycle verifies that disabling VAT stops
// subsequent payments from having VAT applied, and that PaymentSummary only
// reflects VAT from payments made while VAT was enabled.
func TestVAT_DisableVATRegistration_Lifecycle(t *testing.T) {
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to enable VAT: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 100.00 WHERE id = $1", bookingID)

	// Phase 1: Pay £50 cash with VAT enabled — VAT should be applied
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"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(ctx)

	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.Fatalf("phase 1: expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Verify VAT applied on phase 1 payment
	var vat1 sql.NullFloat64
	var net1 sql.NullFloat64
	var vatApplicable1 bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' AND amount = 50.00`, bookingID).Scan(&vatApplicable1, &vat1, &net1)
	if err != nil {
		t.Fatalf("phase 1: failed to query payment: %v", err)
	}
	if !vatApplicable1 {
		t.Error("phase 1: expected is_vat_applicable TRUE when VAT is enabled")
	}
	if !vat1.Valid {
		t.Fatal("phase 1: expected vat_amount to be set")
	}
	if vat1.Float64 != 8.33 {
		t.Errorf("phase 1: expected vat_amount 8.33 (£50 at 20%%), got %.2f", vat1.Float64)
	}
	if !net1.Valid {
		t.Fatal("phase 1: expected net_amount to be set")
	}
	if net1.Float64 != 41.67 {
		t.Errorf("phase 1: expected net_amount 41.67 (£50/1.2), got %.2f", net1.Float64)
	}

	// Phase 2: Disable VAT
	_, err = tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE`)
	if err != nil {
		t.Fatalf("phase 2: failed to disable VAT: %v", err)
	}

	// Phase 3: Pay another £50 cash — should have NO VAT
	reqBody3, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("phase 3: expected 200, got %d: %s", w3.Code, w3.Body.String())
	}

	// Verify NO VAT on the second payment (the latest cash payment)
	var vat3 sql.NullFloat64
	var net3 sql.NullFloat64
	var vatApplicable3 bool
	var amount3 float64
	err = tx.QueryRow(ctx, `SELECT amount, is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' ORDER BY created_at DESC LIMIT 1`, bookingID).Scan(&amount3, &vatApplicable3, &vat3, &net3)
	if err != nil {
		t.Fatalf("phase 3: failed to query latest payment: %v", err)
	}

	if vatApplicable3 {
		t.Error("phase 3: expected is_vat_applicable FALSE after VAT disabled")
	}
	if vat3.Valid {
		t.Errorf("phase 3: expected vat_amount NULL after disabling VAT, got %.2f", vat3.Float64)
	}

	// Phase 4: Verify PaymentSummary only includes VAT from the first payment
	svc := NewPaymentService()
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("phase 4: GetBookingPaymentSummary failed: %v", err)
	}

	if summary.PaidAmount != 100.00 {
		t.Errorf("phase 4: expected PaidAmount 100.00 (50 + 50), got %.2f", summary.PaidAmount)
	}
	if summary.TotalVATAmount != 8.33 {
		t.Errorf("phase 4: expected TotalVATAmount 8.33 (only first payment has VAT), got %.2f", summary.TotalVATAmount)
	}
	// First payment net=41.67, second payment has no VAT so net falls back to amount=50.00
	if summary.TotalNetAmount != 91.67 {
		t.Errorf("phase 4: expected TotalNetAmount 91.67 (41.67 + 50.00), got %.2f", summary.TotalNetAmount)
	}
	if summary.RemainingAmount != 0.00 {
		t.Errorf("phase 4: expected RemainingAmount 0.00 (100.00 - 100.00), got %.2f", summary.RemainingAmount)
	}
}

// TestVAT_DisableAndReEnable_Lifecycle verifies that VAT can be toggled off
// and back on, with payments correctly reflecting the current registration
// state at the time each payment is made.
func TestVAT_DisableAndReEnable_Lifecycle(t *testing.T) {
	// Non-parallel: 47 VAT tests all UPDATE the shared business_settings row.
	// With t.Parallel() + PostgreSQL row locks, tests block each other and
	// this test's multiple state transitions (enable→disable→re-enable) are
	// particularly susceptible to timeout.
	ctx, tx := testutils.SetupTestTx(t)

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to enable VAT: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 200.00 WHERE id = $1", bookingID)

	// Phase 1: VAT enabled — pay £50 cash with VAT
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"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(ctx)

	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.Fatalf("phase 1: expected 200, got %d: %s", w.Code, w.Body.String())
	}

	var p1VAT sql.NullFloat64
	var p1Net sql.NullFloat64
	var p1VATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' AND amount = 50.00`, bookingID).Scan(&p1VATApplicable, &p1VAT, &p1Net)
	if err != nil {
		t.Fatalf("phase 1: failed to query payment: %v", err)
	}
	if !p1VATApplicable {
		t.Error("phase 1: expected VAT applicable when enabled")
	}
	if !p1VAT.Valid {
		t.Fatal("phase 1: expected vat_amount to be set")
	}
	if p1VAT.Float64 != 8.33 {
		t.Errorf("phase 1: expected vat_amount 8.33, got %.2f", p1VAT.Float64)
	}
	if !p1Net.Valid {
		t.Fatal("phase 1: expected net_amount to be set")
	}
	if p1Net.Float64 != 41.67 {
		t.Errorf("phase 1: expected net_amount 41.67, got %.2f", p1Net.Float64)
	}

	// Phase 2: Disable VAT
	_, err = tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = FALSE`)
	if err != nil {
		t.Fatalf("phase 2: failed to disable VAT: %v", err)
	}

	// Pay another £50 cash — should have NO VAT
	reqBody2, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("phase 2: expected 200, got %d: %s", w2.Code, w2.Body.String())
	}

	// Get the latest cash payment (the second one, without VAT)
	var p2VAT sql.NullFloat64
	var p2Net sql.NullFloat64
	var p2VATApplicable bool
	var p2Amount float64
	err = tx.QueryRow(ctx, `SELECT amount, is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' ORDER BY created_at DESC LIMIT 1`, bookingID).Scan(&p2Amount, &p2VATApplicable, &p2VAT, &p2Net)
	if err != nil {
		t.Fatalf("phase 2: failed to query latest payment: %v", err)
	}

	if p2VATApplicable {
		t.Error("phase 2: expected NO VAT after disabling")
	}
	if p2VAT.Valid {
		t.Errorf("phase 2: expected vat_amount NULL, got %.2f", p2VAT.Float64)
	}

	// Phase 3: Re-enable VAT
	_, err = tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE`)
	if err != nil {
		t.Fatalf("phase 3: failed to re-enable VAT: %v", err)
	}

	// Pay another £50 cash — should have VAT again
	reqBody3, _ := json.Marshal(map[string]interface{}{
		"amount":         5000,
		"payment_type":   "full",
		"payment_method": "cash",
	})
	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(ctx)

	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.Fatalf("phase 3: expected 200, got %d: %s", w3.Code, w3.Body.String())
	}

	// Get the latest cash payment (the third one, with VAT re-enabled)
	var p3VAT sql.NullFloat64
	var p3Net sql.NullFloat64
	var p3VATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash' ORDER BY created_at DESC LIMIT 1`, bookingID).Scan(&p3VATApplicable, &p3VAT, &p3Net)
	if err != nil {
		t.Fatalf("phase 3: failed to query latest payment: %v", err)
	}

	if !p3VATApplicable {
		t.Error("phase 3: expected VAT applicable after re-enabling")
	}
	if !p3VAT.Valid {
		t.Fatal("phase 3: expected vat_amount to be set after re-enabling")
	}
	if p3VAT.Float64 != 8.33 {
		t.Errorf("phase 3: expected vat_amount 8.33, got %.2f", p3VAT.Float64)
	}
	if !p3Net.Valid {
		t.Fatal("phase 3: expected net_amount to be set after re-enabling")
	}
	if p3Net.Float64 != 41.67 {
		t.Errorf("phase 3: expected net_amount 41.67, got %.2f", p3Net.Float64)
	}

	// Phase 4: Verify PaymentSummary across all three phases
	svc := NewPaymentService()
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("phase 4: GetBookingPaymentSummary failed: %v", err)
	}

	if summary.PaidAmount != 150.00 {
		t.Errorf("phase 4: expected PaidAmount 150.00 (50×3), got %.2f", summary.PaidAmount)
	}
	// Phase 1: 8.33 VAT, Phase 2: 0 VAT, Phase 3: 8.33 VAT
	if summary.TotalVATAmount != 16.66 {
		t.Errorf("phase 4: expected TotalVATAmount 16.66 (8.33 + 0 + 8.33), got %.2f", summary.TotalVATAmount)
	}
	// Phase 1: 41.67 net, Phase 2: 50.00 (no VAT fallback), Phase 3: 41.67 net
	if summary.TotalNetAmount != 133.34 {
		t.Errorf("phase 4: expected TotalNetAmount 133.34 (41.67 + 50.00 + 41.67), got %.2f", summary.TotalNetAmount)
	}
	if summary.RemainingAmount != 50.00 {
		t.Errorf("phase 4: expected RemainingAmount 50.00 (200.00 - 150.00), got %.2f", summary.RemainingAmount)
	}
}

// ─── VAT + discount + remaining balance ────────────────────────────────────

// TestVAT_DiscountAndCashPayment_RemainingBalance verifies that when a booking
// has a cash payment (with VAT) and a discount payment, the PaymentSummary
// correctly reports TotalVATAmount (only the cash portion), TotalNetAmount,
// PaidAmount, and RemainingAmount (total - paid).
// ─── T7: VAT Rounding Consistency Test ─────────────────────────────────────

// TestVAT_RoundingConsistency verifies that for edge case amounts,
// vat_amount + net_amount = gross_amount exactly (no rounding drift).
func TestVAT_RoundingConsistency(t *testing.T) {
	edgeCases := []struct {
		name   string
		amount float64
	}{
		{"£10.005 rounding boundary", 10.005},
		{"£100.00 exact", 100.00},
		{"£33.33 repeating decimal", 33.33},
		{"£0.01 minimum", 0.01},
		{"£9.99 just under £10", 9.99},
		{"£19.99 just under £20", 19.99},
		{"£99.99 just under £100", 99.99},
		{"£1.00 single unit", 1.00},
		{"£7.50 half", 7.50},
		{"£66.66 repeating", 66.66},
		{"£199.99 large", 199.99},
		{"£0.50 half pound", 0.50},
		{"£0.49 just under half", 0.49},
		{"£0.51 just over half", 0.51},
		{"£999.99 near thousand", 999.99},
	}

	for _, tt := range edgeCases {
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			ctx, tx := testutils.SetupTestTx(t)

			_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
			if err != nil {
				t.Fatalf("failed to update business_settings: %v", err)
			}

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

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

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

			_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)

			// Create a payment directly
			var paymentID string
			err = tx.QueryRow(ctx, `
				INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, created_by, created_at, updated_at)
				VALUES ($1, 'full', 'cash', 'completed', $2, $3, NOW(), NOW())
				RETURNING id
			`, bookingID, tt.amount, adminID).Scan(&paymentID)
			if err != nil {
				t.Fatalf("failed to insert payment: %v", err)
			}

			// Read the actual stored gross amount (DB may round to 2dp)
			var storedGross float64
			err = tx.QueryRow(ctx, "SELECT amount FROM payments WHERE id = $1", paymentID).Scan(&storedGross)
			if err != nil {
				t.Fatalf("failed to read stored amount: %v", err)
			}

			// Apply VAT
			ApplyVATToBookingPayment(ctx, tx, paymentID)

			// Verify vat + net = gross
			var vatAmount sql.NullFloat64
			var netAmount sql.NullFloat64
			var isVATApplicable bool
			err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1`, paymentID).Scan(&isVATApplicable, &vatAmount, &netAmount)

			if !isVATApplicable {
				t.Fatal("expected VAT to be applicable")
			}
			if !vatAmount.Valid {
				t.Fatal("expected vat_amount to be set")
			}
			if !netAmount.Valid {
				t.Fatal("expected net_amount to be set")
			}

			sum := vatAmount.Float64 + netAmount.Float64
			diff := sum - storedGross
			if diff < -0.02 || diff > 0.02 {
				t.Errorf("vat(%.2f) + net(%.2f) = %.2f, expected stored gross %.2f (diff=%.4f)",
					vatAmount.Float64, netAmount.Float64, sum, storedGross, diff)
			} else if diff != 0 {
				t.Logf("minor rounding drift: vat(%.2f) + net(%.2f) = %.2f, stored gross %.2f (diff=%.4f)",
					vatAmount.Float64, netAmount.Float64, sum, storedGross, diff)
			}

			// Verify the payment summary aggregates also hold (with tolerance)
			summary, sErr := svc.GetBookingPaymentSummary(ctx, bookingID)
			if sErr != nil {
				t.Fatalf("GetBookingPaymentSummary failed: %v", sErr)
			}
			summarySum := summary.TotalVATAmount + summary.TotalNetAmount
			summaryDiff := summarySum - summary.PaidAmount
			if summaryDiff < -0.02 || summaryDiff > 0.02 {
				t.Errorf("summary: TotalVATAmount(%.2f) + TotalNetAmount(%.2f) = %.2f, expected PaidAmount %.2f (diff=%.4f)",
					summary.TotalVATAmount, summary.TotalNetAmount, summarySum, summary.PaidAmount, summaryDiff)
			}
		})
	}
}

// ─── T8: SPV vs MPV Lifecycle Tests ─────────────────────────────────────────
// NOTE: SPV and MPV lifecycle tests already exist above:
//   - TestSPV_FullLifecycle_BuyAndRedeem (line ~1334)
//   - TestMPV_FullLifecycle_BuyAndRedeem (line ~1446)
//   - TestVoucherToggle_SPVPurchase_MPVRedeem (line ~2059)
//   - TestVoucherToggle_MPVPurchase_SPVRedeem (line ~2220)
// These comprehensively cover the full lifecycle of both voucher types
// including purchase, redemption, and toggle scenarios.

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

	_, err := tx.Exec(ctx, `UPDATE business_settings SET is_vat_registered = TRUE, default_vat_rate = 20.00`)
	if err != nil {
		t.Fatalf("failed to update business_settings: %v", err)
	}

	adminID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create user: %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)
	}
	_, _ = tx.Exec(ctx, "UPDATE bookings SET status = 'in_progress' WHERE id = $1", bookingID)
	_, _ = tx.Exec(ctx, "UPDATE bookings SET total_amount = 100.00 WHERE id = $1", bookingID)

	// Pay £30 cash — VAT applied via handler
	reqBody, _ := json.Marshal(map[string]interface{}{
		"amount":         3000,
		"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(ctx)

	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.Fatalf("cash payment: expected 200, got %d: %s", w.Code, w.Body.String())
	}

	// Verify cash payment has VAT
	var vatAmount sql.NullFloat64
	var cashNet sql.NullFloat64
	var isVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE booking_id = $1 AND payment_method = 'cash'`, bookingID).Scan(&isVATApplicable, &vatAmount, &cashNet)
	if err != nil {
		t.Fatalf("failed to query cash payment: %v", err)
	}
	if !isVATApplicable {
		t.Error("expected VAT applicable on cash payment")
	}
	if !vatAmount.Valid {
		t.Fatal("expected vat_amount on cash payment")
	}
	if vatAmount.Float64 != 5.00 {
		t.Errorf("expected vat_amount 5.00 (£30 at 20%%), got %.2f", vatAmount.Float64)
	}
	if !cashNet.Valid {
		t.Fatal("expected net_amount on cash payment")
	}
	if cashNet.Float64 != 25.00 {
		t.Errorf("expected net_amount 25.00 (£30/1.2), got %.2f", cashNet.Float64)
	}

	// Add a discount payment directly via the service layer
	discountRecord := PaymentRecord{
		BookingID:     bookingID,
		PaymentType:   "full",
		PaymentMethod: "discount",
		Status:        "completed",
		Amount:        10.00,
		CreatedAt:     clock.Now(),
		UpdatedAt:     clock.Now(),
		CreatedBy:     &adminID,
	}
	svc := NewPaymentService()
	discountID, err := svc.CreatePaymentRecord(ctx, discountRecord, nil)
	if err != nil {
		t.Fatalf("failed to create discount payment: %v", err)
	}

	// Apply VAT to discount payment — should be a no-op
	ApplyVATToBookingPayment(ctx, tx, discountID)

	// Verify discount payment has NO VAT
	var discVAT sql.NullFloat64
	var discNet sql.NullFloat64
	var discVATApplicable bool
	err = tx.QueryRow(ctx, `SELECT is_vat_applicable, vat_amount, net_amount FROM payments WHERE id = $1`, discountID).Scan(&discVATApplicable, &discVAT, &discNet)
	if err != nil {
		t.Fatalf("failed to query discount payment: %v", err)
	}
	if discVATApplicable {
		t.Error("expected discount payment to have is_vat_applicable FALSE")
	}
	if discVAT.Valid {
		t.Errorf("expected vat_amount NULL for discount, got %.2f", discVAT.Float64)
	}

	// Verify PaymentSummary correctness
	summary, err := svc.GetBookingPaymentSummary(ctx, bookingID)
	if err != nil {
		t.Fatalf("GetBookingPaymentSummary failed: %v", err)
	}

	if summary.TotalAmount != 100.00 {
		t.Errorf("expected TotalAmount 100.00, got %.2f", summary.TotalAmount)
	}
	// PaidAmount = £30 cash + £10 discount
	if summary.PaidAmount != 40.00 {
		t.Errorf("expected PaidAmount 40.00 (30 cash + 10 discount), got %.2f", summary.PaidAmount)
	}
	// TotalVATAmount only from cash payment: £30 at 20% → vat=5.00
	if summary.TotalVATAmount != 5.00 {
		t.Errorf("expected TotalVATAmount 5.00 (from cash payment only), got %.2f", summary.TotalVATAmount)
	}
	// TotalNetAmount: cash net=25.00 + discount net falls back to amount=10.00
	if summary.TotalNetAmount != 35.00 {
		t.Errorf("expected TotalNetAmount 35.00 (25.00 cash net + 10.00 discount), got %.2f", summary.TotalNetAmount)
	}
	// RemainingAmount = total - paid = 100.00 - 40.00 = 60.00
	if summary.RemainingAmount != 60.00 {
		t.Errorf("expected RemainingAmount 60.00 (100.00 - 40.00), got %.2f", summary.RemainingAmount)
	}
}
