import React from 'react'

import {
  render,
  type RenderOptions,
  type RenderResult,
} from '@testing-library/react'
import {vi} from 'vitest'

import {createProduct, createShop} from './mocks'

import type {
  Product,
  ProductVariant,
  Shop,
  ProductImage,
  Money,
} from '@shopify/shop-minis-platform'

// Create spyable mock SDK functions
const createSpyableMockSDK = () => {
  return {
    navigateToProduct: vi.fn(),
    navigateToShop: vi.fn(),
    saveProduct: vi.fn().mockResolvedValue({ok: true, data: undefined}),
    unsaveProduct: vi.fn().mockResolvedValue({ok: true, data: undefined}),
    favorite: vi.fn().mockResolvedValue({ok: true, data: undefined}),
    unfavorite: vi.fn().mockResolvedValue({ok: true, data: undefined}),
    followShop: vi.fn().mockResolvedValue({ok: true, data: true}),
    unfollowShop: vi.fn().mockResolvedValue({ok: true, data: false}),
    buyProduct: vi.fn(),
    buyProducts: vi.fn(),
    addToCart: vi.fn(),
    getProduct: vi.fn(),
    getProducts: vi.fn(),
    getProductSearch: vi.fn(),
    getRecommendedProducts: vi.fn(),
    getPopularProducts: vi.fn(),
    getSavedProducts: vi.fn(),
    getRecentProducts: vi.fn(),
    getCuratedProducts: vi.fn(),
    share: vi.fn(),
    closeMini: vi.fn(),
    showErrorScreen: vi.fn(),
    showErrorToast: vi.fn(),
    getAccountInformation: vi.fn(),
    getPersistedItem: vi.fn(),
    setPersistedItem: vi.fn(),
    removePersistedItem: vi.fn(),
    clearPersistedItems: vi.fn(),
  } as any
}

// Mock window.minisSDK for tests
export const mockMinisSDK = createSpyableMockSDK()

// Setup minisSDK mock globally
if (typeof window !== 'undefined') {
  ;(window as any).minisSDK = mockMinisSDK
}

// Custom render with providers if needed
export function renderWithProviders(
  ui: React.ReactElement,
  options?: Omit<RenderOptions, 'wrapper'>
): RenderResult {
  return render(ui, {
    ...options,
  })
}

// Re-export mock factories from mocks.ts with backwards compatibility
export const mockProduct = (overrides: Partial<Product> = {}): Product => {
  const baseProduct = createProduct(
    overrides.id || 'product-1',
    overrides.title || 'Test Product',
    overrides.price?.amount || '99.99',
    overrides.compareAtPrice?.amount
  )
  return {
    ...baseProduct,
    ...overrides,
  }
}

export const mockShop = (overrides: Partial<Shop> = {}): Shop => {
  const baseShop = createShop(
    overrides.id || 'shop-1',
    overrides.name || 'Test Shop'
  )
  return {
    ...baseShop,
    ...overrides,
  }
}

// Helper to create multiple products
export const mockProducts = (count = 5): Product[] => {
  return Array.from({length: count}, (_, i) =>
    createProduct(
      `product-${i + 1}`,
      `Test Product ${i + 1}`,
      `${(i + 1) * 50}.00`
    )
  )
}

// Export commonly used mock data helpers for backwards compatibility
export const mockMoney = (amount = '29.99', currencyCode = 'USD'): Money => ({
  amount,
  currencyCode: currencyCode as any,
})

export const mockProductImage = (
  overrides: Partial<ProductImage> = {}
): ProductImage => ({
  url: 'https://example.com/product-image.jpg',
  altText: 'Product image',
  width: 1000,
  height: 1000,
  sensitive: false,
  thumbhash: 'someThumbhash',
  ...overrides,
})

export const mockProductVariant = (
  overrides: Partial<ProductVariant> = {}
): ProductVariant => ({
  id: 'variant-1',
  title: 'Default Variant',
  isFavorited: false,
  price: mockMoney('29.99', 'USD'),
  compareAtPrice: mockMoney('39.99', 'USD'),
  image: mockProductImage(),
  ...overrides,
})

// Helper to wait for async updates
export const waitForAsync = () => new Promise(resolve => setTimeout(resolve, 0))

// Common test assertions for accessibility
export const expectToBeAccessible = (element: HTMLElement) => {
  // Check for basic accessibility attributes
  const role = element.getAttribute('role')
  const ariaLabel = element.getAttribute('aria-label')
  const ariaLabelledBy = element.getAttribute('aria-labelledby')

  // At least one of these should be present for interactive elements
  if (element.tagName === 'BUTTON' || element.onclick) {
    const hasAccessibility = Boolean(
      role || ariaLabel || ariaLabelledBy || element.textContent?.trim()
    )
    expect(hasAccessibility).toBe(true)
  }
}

// Reset all mocks
export const resetAllMocks = () => {
  Object.values(mockMinisSDK).forEach(mock => {
    if (typeof mock === 'function' && 'mockReset' in mock) {
      ;(mock as any).mockReset()
    }
  })
}

// Export everything from testing library for convenience
export * from '@testing-library/react'
export {default as userEvent} from '@testing-library/user-event'
