//go:build test
// +build test

package fixtures

import (
	"context"
	"fmt"
	"sync/atomic"
	"time"

	"crussell/db"
	"golang.org/x/crypto/bcrypt"
)

// Global counter for unique emails in tests
var testEmailCounter atomic.Int64

func CreateTestAdminUser(q db.Querier) (string, error) {
	return createTestUser(q, "Admin", "User", "", "admin")
}

func CreateTestUser(q db.Querier) (string, error) {
	return createTestUser(q, "Test", "User", "", "verified_email")
}

func CreateTestUserWithEmail(q db.Querier, email, role string) (string, error) {
	return createTestUser(q, "Test", "User", email, role)
}

func createTestUser(q db.Querier, firstName, lastName, email, role string) (string, error) {
	passwordHash, err := bcrypt.GenerateFromPassword([]byte("testpassword123"), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("failed to hash password: %w", err)
	}

	// Generate unique email if not provided
	if email == "" {
		n := testEmailCounter.Add(1)
		email = fmt.Sprintf("%s.%s.%d@test.com", firstName, lastName, n)
	}

	ctx := context.Background()
	var userID string
	err = q.QueryRow(ctx, `
		INSERT INTO users (n_first_name, n_last_name, email, phone, date_of_birth, password_hash, account_role, account_type)
		VALUES ($1, $2, $3, $4, $5, $6, $7, 'email')
		RETURNING id
	`, firstName, lastName, email, "+447123456789", "1990-01-01", string(passwordHash), role).Scan(&userID)

	if err != nil {
		return "", fmt.Errorf("failed to create user: %w", err)
	}

	return userID, nil
}

func CreateTestService(q db.Querier) (string, error) {
	ctx := context.Background()
	var serviceID string
	err := q.QueryRow(ctx, `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, "Test Service", "A test service for unit tests", 50.00, 60, true, 16).Scan(&serviceID)

	if err != nil {
		return "", fmt.Errorf("failed to create service: %w", err)
	}

	return serviceID, nil
}

func CreateTestServiceWithDuration(q db.Querier, durationMinutes int) (string, error) {
	ctx := context.Background()
	var serviceID string
	err := q.QueryRow(ctx, `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, fmt.Sprintf("Test Service %dmin", durationMinutes), "A test service for unit tests", 50.00, durationMinutes, true, 16).Scan(&serviceID)
	if err != nil {
		return "", fmt.Errorf("failed to create service: %w", err)
	}
	return serviceID, nil
}

// CreateTestServiceWithPatchTest creates a service and a patch test that links to it
// Returns serviceID, patchTestID
func CreateTestServiceWithPatchTest(q db.Querier) (string, string, error) {
	ctx := context.Background()

	// First create the service
	var serviceID string
	err := q.QueryRow(ctx, `
		INSERT INTO services (name, description, price, duration_minutes, is_active, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5, $6)
		RETURNING id
	`, "Test Patch Test Service", "A test service requiring patch test", 75.00, 90, true, 18).Scan(&serviceID)

	if err != nil {
		return "", "", fmt.Errorf("failed to create service: %w", err)
	}

	// Now create a patch test that links to this service
	var patchTestID string
	err = q.QueryRow(ctx, `
		INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids)
		VALUES ($1, $2, $3, $4, $5)
		RETURNING id
	`, "Test Patch Test", "A patch test for testing", 24, 6, []string{serviceID}).Scan(&patchTestID)

	if err != nil {
		return "", "", fmt.Errorf("failed to create patch test: %w", err)
	}

	return serviceID, patchTestID, nil
}

// CreateTestPatchTest creates a patch test definition
func CreateTestPatchTest(q db.Querier, serviceIDs []string) (string, error) {
	ctx := context.Background()
	var patchTestID string
	err := q.QueryRow(ctx, `
		INSERT INTO patch_tests (name, description, notice_duration_hours, expiry_months, service_ids)
		VALUES ($1, $2, $3, $4, $5)
		RETURNING id
	`, "Test Patch Test", "A patch test for testing", 24, 6, serviceIDs).Scan(&patchTestID)

	if err != nil {
		return "", fmt.Errorf("failed to create patch test: %w", err)
	}

	return patchTestID, nil
}

// CreateUserPatchTest creates a user patch test record
func CreateUserPatchTest(q db.Querier, userID, patchTestID string, testedAt string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, `
		INSERT INTO user_patch_tests (user_id, patch_test_id, tested_at)
		VALUES ($1, $2, $3)
	`, userID, patchTestID, testedAt)

	if err != nil {
		return fmt.Errorf("failed to create user patch test: %w", err)
	}

	return nil
}

func CreateTestBooking(q db.Querier, userID, serviceID string) (string, error) {
	return CreateTestBookingAtTime(q, userID, serviceID, time.Date(2099, 12, 31, 10, 0, 0, 0, time.UTC))
}

func CreateTestBookingAtTime(q db.Querier, userID, serviceID string, startTime time.Time) (string, error) {
	ctx := context.Background()
	var bookingID string
	err := q.QueryRow(ctx, `
		INSERT INTO bookings (user_id, start_time, status, notes)
		VALUES ($1, $2, $3, $4)
		RETURNING id
	`, userID, startTime, "pending", "Test booking").Scan(&bookingID)

	if err != nil {
		return "", fmt.Errorf("failed to create booking: %w", err)
	}

	_, err = q.Exec(ctx, `
		INSERT INTO booking_services (booking_id, service_id)
		VALUES ($1, $2)
	`, bookingID, serviceID)

	if err != nil {
		return "", fmt.Errorf("failed to link service to booking: %w", err)
	}

	return bookingID, nil
}

func CreateTestVerifiedUser(q db.Querier) (string, error) {
	return createTestUser(q, "Verified", "User", "verified@test.com", "verified_email")
}

func CreateTestUnverifiedUser(q db.Querier) (string, error) {
	return createTestUser(q, "Unverified", "User", "unverified@test.com", "unverified_email")
}

func CreateTestGuestUser(q db.Querier) (string, error) {
	return createTestUser(q, "Guest", "User", "guest@test.com", "guest")
}

func DeleteUser(q db.Querier, userID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM users WHERE id = $1", userID)
	return err
}

func DeleteService(q db.Querier, serviceID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM services WHERE id = $1", serviceID)
	return err
}

func DeleteBooking(q db.Querier, bookingID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM bookings WHERE id = $1", bookingID)
	return err
}

func CreateTestCustomService(q db.Querier) (string, error) {
	ctx := context.Background()
	var serviceID string
	err := q.QueryRow(ctx, `
		INSERT INTO custom_services (name, description, price, duration_minutes, minimum_age_required)
		VALUES ($1, $2, $3, $4, $5)
		RETURNING id
	`, "Test Custom Service", "A test custom service", 75.00, 45, 16).Scan(&serviceID)

	if err != nil {
		return "", fmt.Errorf("failed to create custom service: %w", err)
	}

	return serviceID, nil
}

func DeleteCustomService(q db.Querier, serviceID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM custom_services WHERE id = $1", serviceID)
	return err
}

// SafeDeleteUser wraps DeleteUser and returns error (for tests that care about cleanup failure)
func SafeDeleteUser(q db.Querier, userID string) error {
	return DeleteUser(q, userID)
}

// SafeDeleteService wraps DeleteService and returns error (for tests that care about cleanup failure)
func SafeDeleteService(q db.Querier, serviceID string) error {
	return DeleteService(q, serviceID)
}

// SafeDeleteBooking wraps DeleteBooking and returns error (for tests that care about cleanup failure)
func SafeDeleteBooking(q db.Querier, bookingID string) error {
	return DeleteBooking(q, bookingID)
}

// CreateTestTimeBlocker creates a time blocker for testing
// Returns the blocker ID
func CreateTestTimeBlocker(q db.Querier, startTime time.Time, durationMinutes int, description string) (string, error) {
	ctx := context.Background()
	var blockerID string
	err := q.QueryRow(ctx, `
		INSERT INTO time_blockers (start_time, duration_minutes, description)
		VALUES ($1, $2, $3)
		RETURNING id
	`, startTime, durationMinutes, description).Scan(&blockerID)

	if err != nil {
		return "", fmt.Errorf("failed to create time blocker: %w", err)
	}

	return blockerID, nil
}

// DeleteTimeBlocker removes a time blocker from the database
func DeleteTimeBlocker(q db.Querier, blockerID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM time_blockers WHERE id = $1", blockerID)
	return err
}

// CreateTestPayment creates a payment record for testing
// Returns payment ID
func CreateTestPayment(q db.Querier, bookingID string, amount float64, method string, ptype string, status string) (string, error) {
	ctx := context.Background()
	var paymentID string
	err := q.QueryRow(ctx, `
		INSERT INTO payments (booking_id, payment_type, payment_method, status, amount, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, NOW(), NOW())
		RETURNING id
	`, bookingID, ptype, method, status, amount).Scan(&paymentID)

	if err != nil {
		return "", fmt.Errorf("failed to create payment: %w", err)
	}

	return paymentID, nil
}

// CreateTestRefund creates a refund record for testing
// Returns refund ID
func CreateTestRefund(q db.Querier, paymentID string, bookingID string, amount float64) (string, error) {
	ctx := context.Background()
	var refundID string
	err := q.QueryRow(ctx, `
		INSERT INTO refunds (payment_id, booking_id, amount, status, reason, created_at)
		VALUES ($1, $2, $3, 'completed', 'test refund', NOW())
		RETURNING id
	`, paymentID, bookingID, amount).Scan(&refundID)

	if err != nil {
		return "", fmt.Errorf("failed to create refund: %w", err)
	}

	return refundID, nil
}

// CreateTestPaymentMethod creates a saved card for a user
// Returns card ID
func CreateTestPaymentMethod(q db.Querier, userID string, squareCardID string, brand string, last4 string) (string, error) {
	ctx := context.Background()
	var cardID string
	err := q.QueryRow(ctx, `
		INSERT INTO user_saved_cards (user_id, square_card_id, brand, last_4, exp_month, exp_year, fingerprint, is_default, created_at)
		VALUES ($1, $2, $3, $4, 12, 2030, 'test_fp', false, NOW())
		RETURNING id
	`, userID, squareCardID, brand, last4).Scan(&cardID)

	if err != nil {
		return "", fmt.Errorf("failed to create payment method: %w", err)
	}

	return cardID, nil
}

// DeletePayment deletes a payment from the database
func DeletePayment(q db.Querier, paymentID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM payments WHERE id = $1", paymentID)
	return err
}

// DeleteRefund deletes a refund from the database
func DeleteRefund(q db.Querier, refundID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM refunds WHERE id = $1", refundID)
	return err
}

// DeletePaymentMethod deletes a saved card from the database
func DeletePaymentMethod(q db.Querier, cardID string) error {
	ctx := context.Background()
	_, err := q.Exec(ctx, "DELETE FROM user_saved_cards WHERE id = $1", cardID)
	return err
}
