/**
 * Test Template for React Components
 *
 * WHY THIS STRUCTURE?
 * - Organized sections make tests easy to navigate and maintain
 * - Mocks at top ensure consistent test isolation
 * - Factory functions reduce duplication and improve readability
 * - describe blocks group related scenarios for better debugging
 *
 * INSTRUCTIONS:
 * 1. Replace `ComponentName` with your component name
 * 2. Update import path
 * 3. Add/remove test sections based on component features (use analyze-component)
 * 4. Follow AAA pattern: Arrange → Act → Assert
 *
 * RUN FIRST: pnpm analyze-component <path> to identify required test scenarios
 */

import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
// import ComponentName from './index'

// ============================================================================
// Mocks
// ============================================================================
// WHY: Mocks must be hoisted to top of file (Jest requirement).
// They run BEFORE imports, so keep them before component imports.

// i18n (always required in Dify)
// WHY: Returns key instead of translation so tests don't depend on i18n files
jest.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string) => key,
  }),
}))

// Router (if component uses useRouter, usePathname, useSearchParams)
// WHY: Isolates tests from Next.js routing, enables testing navigation behavior
// const mockPush = jest.fn()
// jest.mock('next/navigation', () => ({
//   useRouter: () => ({ push: mockPush }),
//   usePathname: () => '/test-path',
// }))

// API services (if component fetches data)
// WHY: Prevents real network calls, enables testing all states (loading/success/error)
// jest.mock('@/service/api')
// import * as api from '@/service/api'
// const mockedApi = api as jest.Mocked<typeof api>

// Shared mock state (for portal/dropdown components)
// WHY: Portal components like PortalToFollowElem need shared state between
// parent and child mocks to correctly simulate open/close behavior
// let mockOpenState = false

// ============================================================================
// Test Data Factories
// ============================================================================
// WHY FACTORIES?
// - Avoid hard-coded test data scattered across tests
// - Easy to create variations with overrides
// - Type-safe when using actual types from source
// - Single source of truth for default test values

// const createMockProps = (overrides = {}) => ({
//   // Default props that make component render successfully
//   ...overrides,
// })

// const createMockItem = (overrides = {}) => ({
//   id: 'item-1',
//   name: 'Test Item',
//   ...overrides,
// })

// ============================================================================
// Test Helpers
// ============================================================================

// const renderComponent = (props = {}) => {
//   return render(<ComponentName {...createMockProps(props)} />)
// }

// ============================================================================
// Tests
// ============================================================================

describe('ComponentName', () => {
  // WHY beforeEach with clearAllMocks?
  // - Ensures each test starts with clean slate
  // - Prevents mock call history from leaking between tests
  // - MUST be beforeEach (not afterEach) to reset BEFORE assertions like toHaveBeenCalledTimes
  beforeEach(() => {
    jest.clearAllMocks()
    // Reset shared mock state if used (CRITICAL for portal/dropdown tests)
    // mockOpenState = false
  })

  // --------------------------------------------------------------------------
  // Rendering Tests (REQUIRED - Every component MUST have these)
  // --------------------------------------------------------------------------
  // WHY: Catches import errors, missing providers, and basic render issues
  describe('Rendering', () => {
    it('should render without crashing', () => {
      // Arrange - Setup data and mocks
      // const props = createMockProps()

      // Act - Render the component
      // render(<ComponentName {...props} />)

      // Assert - Verify expected output
      // Prefer getByRole for accessibility; it's what users "see"
      // expect(screen.getByRole('...')).toBeInTheDocument()
    })

    it('should render with default props', () => {
      // WHY: Verifies component works without optional props
      // render(<ComponentName />)
      // expect(screen.getByText('...')).toBeInTheDocument()
    })
  })

  // --------------------------------------------------------------------------
  // Props Tests (REQUIRED - Every component MUST test prop behavior)
  // --------------------------------------------------------------------------
  // WHY: Props are the component's API contract. Test them thoroughly.
  describe('Props', () => {
    it('should apply custom className', () => {
      // WHY: Common pattern in Dify - components should merge custom classes
      // render(<ComponentName className="custom-class" />)
      // expect(screen.getByTestId('component')).toHaveClass('custom-class')
    })

    it('should use default values for optional props', () => {
      // WHY: Verifies TypeScript defaults work at runtime
      // render(<ComponentName />)
      // expect(screen.getByRole('...')).toHaveAttribute('...', 'default-value')
    })
  })

  // --------------------------------------------------------------------------
  // User Interactions (if component has event handlers - on*, handle*)
  // --------------------------------------------------------------------------
  // WHY: Event handlers are core functionality. Test from user's perspective.
  describe('User Interactions', () => {
    it('should call onClick when clicked', async () => {
      // WHY userEvent over fireEvent?
      // - userEvent simulates real user behavior (focus, hover, then click)
      // - fireEvent is lower-level, doesn't trigger all browser events
      // const user = userEvent.setup()
      // const handleClick = jest.fn()
      // render(<ComponentName onClick={handleClick} />)
      //
      // await user.click(screen.getByRole('button'))
      //
      // expect(handleClick).toHaveBeenCalledTimes(1)
    })

    it('should call onChange when value changes', async () => {
      // const user = userEvent.setup()
      // const handleChange = jest.fn()
      // render(<ComponentName onChange={handleChange} />)
      //
      // await user.type(screen.getByRole('textbox'), 'new value')
      //
      // expect(handleChange).toHaveBeenCalled()
    })
  })

  // --------------------------------------------------------------------------
  // State Management (if component uses useState/useReducer)
  // --------------------------------------------------------------------------
  // WHY: Test state through observable UI changes, not internal state values
  describe('State Management', () => {
    it('should update state on interaction', async () => {
      // WHY test via UI, not state?
      // - State is implementation detail; UI is what users see
      // - If UI works correctly, state must be correct
      // const user = userEvent.setup()
      // render(<ComponentName />)
      //
      // // Initial state - verify what user sees
      // expect(screen.getByText('Initial')).toBeInTheDocument()
      //
      // // Trigger state change via user action
      // await user.click(screen.getByRole('button'))
      //
      // // New state - verify UI updated
      // expect(screen.getByText('Updated')).toBeInTheDocument()
    })
  })

  // --------------------------------------------------------------------------
  // Async Operations (if component fetches data - useSWR, useQuery, fetch)
  // --------------------------------------------------------------------------
  // WHY: Async operations have 3 states users experience: loading, success, error
  describe('Async Operations', () => {
    it('should show loading state', () => {
      // WHY never-resolving promise?
      // - Keeps component in loading state for assertion
      // - Alternative: use fake timers
      // mockedApi.fetchData.mockImplementation(() => new Promise(() => {}))
      // render(<ComponentName />)
      //
      // expect(screen.getByText(/loading/i)).toBeInTheDocument()
    })

    it('should show data on success', async () => {
      // WHY waitFor?
      // - Component updates asynchronously after fetch resolves
      // - waitFor retries assertion until it passes or times out
      // mockedApi.fetchData.mockResolvedValue({ items: ['Item 1'] })
      // render(<ComponentName />)
      //
      // await waitFor(() => {
      //   expect(screen.getByText('Item 1')).toBeInTheDocument()
      // })
    })

    it('should show error on failure', async () => {
      // mockedApi.fetchData.mockRejectedValue(new Error('Network error'))
      // render(<ComponentName />)
      //
      // await waitFor(() => {
      //   expect(screen.getByText(/error/i)).toBeInTheDocument()
      // })
    })
  })

  // --------------------------------------------------------------------------
  // Edge Cases (REQUIRED - Every component MUST handle edge cases)
  // --------------------------------------------------------------------------
  // WHY: Real-world data is messy. Components must handle:
  // - Null/undefined from API failures or optional fields
  // - Empty arrays/strings from user clearing data
  // - Boundary values (0, MAX_INT, special characters)
  describe('Edge Cases', () => {
    it('should handle null value', () => {
      // WHY test null specifically?
      // - API might return null for missing data
      // - Prevents "Cannot read property of null" in production
      // render(<ComponentName value={null} />)
      // expect(screen.getByText(/no data/i)).toBeInTheDocument()
    })

    it('should handle undefined value', () => {
      // WHY test undefined separately from null?
      // - TypeScript treats them differently
      // - Optional props are undefined, not null
      // render(<ComponentName value={undefined} />)
      // expect(screen.getByText(/no data/i)).toBeInTheDocument()
    })

    it('should handle empty array', () => {
      // WHY: Empty state often needs special UI (e.g., "No items yet")
      // render(<ComponentName items={[]} />)
      // expect(screen.getByText(/empty/i)).toBeInTheDocument()
    })

    it('should handle empty string', () => {
      // WHY: Empty strings are truthy in JS but visually empty
      // render(<ComponentName text="" />)
      // expect(screen.getByText(/placeholder/i)).toBeInTheDocument()
    })
  })

  // --------------------------------------------------------------------------
  // Accessibility (optional but recommended for Dify's enterprise users)
  // --------------------------------------------------------------------------
  // WHY: Dify has enterprise customers who may require accessibility compliance
  describe('Accessibility', () => {
    it('should have accessible name', () => {
      // WHY getByRole with name?
      // - Tests that screen readers can identify the element
      // - Enforces proper labeling practices
      // render(<ComponentName label="Test Label" />)
      // expect(screen.getByRole('button', { name: /test label/i })).toBeInTheDocument()
    })

    it('should support keyboard navigation', async () => {
      // WHY: Some users can't use a mouse
      // const user = userEvent.setup()
      // render(<ComponentName />)
      //
      // await user.tab()
      // expect(screen.getByRole('button')).toHaveFocus()
    })
  })
})
