/**
 * UI Consistency Tests
 *
 * Tests for UI component consistency, form validation,
 * loading/error states, and responsive behavior
 */

import type { ReactNode } from 'react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, expect, it, vi } from 'vitest'

// =============================================================================
// UI Component Consistency Tests
// =============================================================================

describe('PageHeader Component', () => {
  const mockBreadcrumb = [
    { title: '首页', path: '/' },
    { title: '用户管理', path: '/users' },
    { title: '用户列表' },
  ]

  it('renders title correctly', () => {
    const { getByText } = render(
      <PageHeaderTest title="用户列表" />
    )
    expect(getByText('用户列表')).toBeInTheDocument()
  })

  it('renders description when provided', () => {
    const { getByText } = render(
      <PageHeaderTest
        title="用户列表"
        description="管理系统中的所有用户"
      />
    )
    expect(getByText('用户列表')).toBeInTheDocument()
    expect(getByText('管理系统中的所有用户')).toBeInTheDocument()
  })

  it('renders breadcrumb when provided', () => {
    const { getByTestId } = render(
      <PageHeaderTest
        title="用户列表"
        breadcrumb={mockBreadcrumb}
      />
    )
    // Breadcrumb renders with text content (may have HTML encoding)
    const breadcrumb = getByTestId('breadcrumb')
    expect(breadcrumb).toHaveTextContent('首页')
    expect(breadcrumb).toHaveTextContent('用户管理')
    expect(breadcrumb).toHaveTextContent('用户列表')
  })

  it('renders actions when provided', () => {
    const { getByText } = render(
      <PageHeaderTest
        title="用户列表"
        actions={<button>新建用户</button>}
      />
    )
    expect(getByText('新建用户')).toBeInTheDocument()
  })

  it('does not render footer when not provided', () => {
    const { queryByText } = render(
      <PageHeaderTest title="用户列表" />
    )
    expect(queryByText('footer')).not.toBeInTheDocument()
  })

  it('renders footer when provided', () => {
    const { getByText } = render(
      <PageHeaderTest
        title="用户列表"
        footer={<div>页脚内容</div>}
      />
    )
    expect(getByText('页脚内容')).toBeInTheDocument()
  })
})

// =============================================================================
// Form Validation Consistency Tests
// =============================================================================

describe('Form Validation Consistency', () => {
  beforeEach(() => {
    vi.spyOn(window, 'alert').mockImplementation(() => {})
  })

  it('validates required fields', async () => {
    const user = userEvent.setup()
    const handleSubmit = vi.fn()

    const TestForm = ({ onSubmit }: { onSubmit: (data: Record<string, string>) => void }) => (
      <form onSubmit={(e) => {
        e.preventDefault()
        const formData = new FormData(e.target as HTMLFormElement)
        const data: Record<string, string> = {}
        formData.forEach((value, key) => {
          if (typeof value === 'string') data[key] = value
        })
        // Check required fields
        if (!data.username || !data.email) return
        onSubmit(data)
      }}>
        <input name="username" required aria-label="用户名" />
        <input name="email" type="email" required aria-label="邮箱" />
        <button type="submit">提交</button>
      </form>
    )

    render(<TestForm onSubmit={handleSubmit} />)

    await user.click(screen.getByRole('button', { name: '提交' }))
    expect(handleSubmit).not.toHaveBeenCalled()
  })

  it('validates email format', async () => {
    const user = userEvent.setup()
    const handleSubmit = vi.fn()

    const TestForm = ({ onSubmit }: { onSubmit: (data: Record<string, string>) => void }) => (
      <form onSubmit={(e) => {
        e.preventDefault()
        const formData = new FormData(e.target as HTMLFormElement)
        const data: Record<string, string> = {}
        formData.forEach((value, key) => {
          if (typeof value === 'string') data[key] = value
        })
        // Simple email validation
        if (!data.email || !data.email.includes('@')) return
        onSubmit(data)
      }}>
        <input name="email" type="email" aria-label="邮箱" />
        <button type="submit">提交</button>
      </form>
    )

    render(<TestForm onSubmit={handleSubmit} />)

    const emailInput = screen.getByRole('textbox', { name: '邮箱' })
    await user.clear(emailInput)
    await user.type(emailInput, 'invalid-email')
    await user.click(screen.getByRole('button', { name: '提交' }))
    expect(handleSubmit).not.toHaveBeenCalled()
  })

  it('validates password strength', async () => {
    const user = userEvent.setup()
    const handleSubmit = vi.fn()

    const TestPasswordForm = ({ onSubmit }: { onSubmit: (data: Record<string, string>) => void }) => {
      const validatePassword = (pwd: string) => {
        if (pwd.length < 8) return '密码长度不能少于8位'
        if (!/[A-Z]/.test(pwd)) return '密码必须包含大写字母'
        if (!/[a-z]/.test(pwd)) return '密码必须包含小写字母'
        if (!/[0-9]/.test(pwd)) return '密码必须包含数字'
        return null
      }

      return (
        <form onSubmit={(e) => {
          e.preventDefault()
          const formData = new FormData(e.target as HTMLFormElement)
          const password = formData.get('password') as string
          const error = validatePassword(password)
          if (error) {
            alert(error)
            return
          }
          onSubmit({ password })
        }}>
          <label htmlFor="password-input">密码</label>
          <input id="password-input" name="password" type="password" />
          <button type="submit">提交</button>
        </form>
      )
    }

    render(<TestPasswordForm onSubmit={handleSubmit} />)

    const pwdInput = screen.getByLabelText('密码')
    await user.clear(pwdInput)
    await user.type(pwdInput, 'weak')
    await user.click(screen.getByRole('button', { name: '提交' }))
    expect(handleSubmit).not.toHaveBeenCalled()
  })

  it('confirms password match', async () => {
    const user = userEvent.setup()
    const handleSubmit = vi.fn()

    const TestConfirmForm = ({ onSubmit }: { onSubmit: () => void }) => (
      <form onSubmit={(e) => {
        e.preventDefault()
        const formData = new FormData(e.target as HTMLFormElement)
        const password = formData.get('password') as string
        const confirm = formData.get('confirm') as string
        if (password !== confirm) {
          alert('两次输入的密码不一致')
          return
        }
        onSubmit()
      }}>
        <label htmlFor="password-input">密码</label>
        <input id="password-input" name="password" type="password" />
        <label htmlFor="confirm-input">确认密码</label>
        <input id="confirm-input" name="confirm" type="password" />
        <button type="submit">提交</button>
      </form>
    )

    render(<TestConfirmForm onSubmit={handleSubmit} />)

    await user.type(screen.getByLabelText('密码'), 'Pass123!')
    await user.type(screen.getByLabelText('确认密码'), 'Different123!')
    await user.click(screen.getByRole('button', { name: '提交' }))
    expect(handleSubmit).not.toHaveBeenCalled()
  })
})

// =============================================================================
// Loading/Error/Empty States Tests
// =============================================================================

describe('Loading States', () => {
  it('shows loading indicator during data fetch', async () => {
    const TestLoadingComponent = ({ isLoading }: { isLoading: boolean }) => (
      <div>
        {isLoading ? <div data-testid="loading">加载中...</div> : <div>数据加载完成</div>}
      </div>
    )

    const { getByTestId, rerender } = render(<TestLoadingComponent isLoading={true} />)
    expect(getByTestId('loading')).toBeInTheDocument()
    expect(getByTestId('loading')).toHaveTextContent('加载中...')

    rerender(<TestLoadingComponent isLoading={false} />)
    await waitFor(() => {
      expect(screen.queryByTestId('loading')).not.toBeInTheDocument()
    })
  })

  it('disables buttons during submission', () => {
    const TestSubmitButton = ({ isSubmitting }: { isSubmitting: boolean }) => (
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? '提交中...' : '提交'}
      </button>
    )

    const { getByRole, rerender } = render(<TestSubmitButton isSubmitting={false} />)
    expect(getByRole('button')).not.toBeDisabled()

    rerender(<TestSubmitButton isSubmitting={true} />)
    expect(getByRole('button')).toBeDisabled()
  })
})

describe('Error States', () => {
  it('displays error message when fetch fails', () => {
    const TestErrorDisplay = ({ error }: { error: string | null }) => (
      <div>
        {error && <div data-testid="error-message">{error}</div>}
      </div>
    )

    const { getByTestId, rerender } = render(<TestErrorDisplay error={null} />)
    expect(screen.queryByTestId('error-message')).not.toBeInTheDocument()

    rerender(<TestErrorDisplay error="网络错误，请稍后重试" />)
    expect(getByTestId('error-message')).toHaveTextContent('网络错误，请稍后重试')
  })

  it('shows retry option after error', () => {
    const handleRetry = vi.fn()

    const TestRetryButton = ({ onRetry }: { onRetry: () => void }) => (
      <div>
        <div>加载失败</div>
        <button onClick={onRetry}>重试</button>
      </div>
    )

    render(<TestRetryButton onRetry={handleRetry} />)
    expect(screen.getByRole('button', { name: '重试' })).toBeInTheDocument()
  })
})

describe('Empty States', () => {
  it('displays empty message when no data', () => {
    const TestEmptyDisplay = ({ items }: { items: unknown[] }) => (
      <div>
        {items.length === 0 ? (
          <div data-testid="empty">暂无数据</div>
        ) : (
          <div>{items.length} 条数据</div>
        )}
      </div>
    )

    const { getByTestId } = render(<TestEmptyDisplay items={[]} />)
    expect(getByTestId('empty')).toHaveTextContent('暂无数据')
  })

  it('shows add action in empty state', () => {
    const TestEmptyStateWithAction = ({ onAdd }: { onAdd: () => void }) => (
      <div>
        <div>暂无数据</div>
        <button onClick={onAdd}>添加第一条数据</button>
      </div>
    )

    const handleAdd = vi.fn()
    render(<TestEmptyStateWithAction onAdd={handleAdd} />)
    expect(screen.getByRole('button', { name: '添加第一条数据' })).toBeInTheDocument()
  })
})

// =============================================================================
// Responsive Behavior Tests
// =============================================================================

describe('Responsive Behavior', () => {
  it('hides secondary content on small screens', () => {
    const TestResponsiveLayout = ({ isMobile }: { isMobile: boolean }) => (
      <div>
        <div data-testid="primary">主要导航</div>
        {isMobile ? null : <div data-testid="secondary">次要内容</div>}
      </div>
    )

    const { getByTestId, rerender } = render(<TestResponsiveLayout isMobile={false} />)
    expect(getByTestId('primary')).toBeInTheDocument()
    expect(getByTestId('secondary')).toBeInTheDocument()

    rerender(<TestResponsiveLayout isMobile={true} />)
    expect(getByTestId('primary')).toBeInTheDocument()
    expect(screen.queryByTestId('secondary')).not.toBeInTheDocument()
  })

  it('collapses table columns on mobile', () => {
    const TestTable = ({ isMobile, columns }: { isMobile: boolean; columns: string[] }) => (
      <table>
        <thead>
          <tr>
            {columns.map((col, i) => (
              <th key={col} data-mobile-only={isMobile && i > 2}>
                {col}
              </th>
            ))}
          </tr>
        </thead>
      </table>
    )

    const { rerender } = render(
      <TestTable isMobile={false} columns={['ID', '用户名', '邮箱', '操作']} />
    )
    const headers = screen.getAllByRole('columnheader')
    expect(headers).toHaveLength(4)

    rerender(<TestTable isMobile={true} columns={['ID', '用户名', '邮箱', '操作']} />)
    // On mobile, only essential columns shown (first 3)
    const mobileOnlyHeaders = screen.getAllByRole('columnheader')
    expect(mobileOnlyHeaders.length).toBeLessThanOrEqual(4)
  })
})

// =============================================================================
// Accessibility Tests
// =============================================================================

describe('Accessibility', () => {
  it('form inputs have accessible labels', () => {
    const TestAccessibleForm = () => (
      <form>
        <label htmlFor="username">用户名</label>
        <input id="username" type="text" />
        <button type="submit">提交</button>
      </form>
    )

    render(<TestAccessibleForm />)
    const input = screen.getByLabelText('用户名')
    expect(input).toHaveAttribute('id', 'username')
  })

  it('buttons have accessible names', () => {
    const TestButton = () => (
      <button type="button" aria-label="关闭对话框">
        <span>×</span>
      </button>
    )

    render(<TestButton />)
    expect(screen.getByRole('button', { name: '关闭对话框' })).toBeInTheDocument()
  })

  it('error messages are announced to screen readers', () => {
    const TestErrorAnnouncement = ({ error }: { error: string }) => (
      <div role="alert">
        {error}
      </div>
    )

    const { getByRole } = render(<TestErrorAnnouncement error="表单验证失败" />)
    expect(getByRole('alert')).toHaveTextContent('表单验证失败')
  })

  it('modal has proper focus management', () => {
    const TestModal = ({ isOpen }: { isOpen: boolean }) => {
      const modalRef = { current: null }

      return (
        <div>
          <button>打开对话框</button>
          {isOpen && (
            <div role="dialog" aria-modal="true" ref={modalRef}>
              <h2>对话框标题</h2>
              <button>关闭</button>
            </div>
          )}
        </div>
      )
    }

    const { rerender } = render(<TestModal isOpen={false} />)
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument()

    rerender(<TestModal isOpen={true} />)
    const dialog = screen.getByRole('dialog')
    expect(dialog).toHaveAttribute('aria-modal', 'true')
    expect(dialog).toHaveTextContent('对话框标题')
  })
})

// =============================================================================
// Data Display Consistency Tests
// =============================================================================

describe('Data Display Consistency', () => {
  it('formats dates consistently', () => {
    const formatDate = (date: Date) => {
      return date.toLocaleDateString('zh-CN', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
      })
    }

    const testDate = new Date('2024-01-15T10:30:00')
    expect(formatDate(testDate)).toBe('2024/01/15 10:30')
  })

  it('formats numbers with thousand separators', () => {
    const formatNumber = (num: number) => {
      return num.toLocaleString('zh-CN')
    }

    expect(formatNumber(1234567)).toBe('1,234,567')
  })

  it('displays status badges with consistent colors', () => {
    const StatusBadge = ({ status }: { status: 'active' | 'inactive' | 'locked' }) => {
      const colors = {
        active: 'green',
        inactive: 'gray',
        locked: 'red',
      }
      return <span data-color={colors[status]}>{status}</span>
    }

    const { getByText } = render(
      <>
        <StatusBadge status="active" />
        <StatusBadge status="inactive" />
        <StatusBadge status="locked" />
      </>
    )

    expect(getByText('active')).toHaveAttribute('data-color', 'green')
    expect(getByText('inactive')).toHaveAttribute('data-color', 'gray')
    expect(getByText('locked')).toHaveAttribute('data-color', 'red')
  })

  it('truncates long text with ellipsis', () => {
    const truncateText = (text: string, maxLength: number) => {
      if (text.length <= maxLength) return text
      return text.slice(0, maxLength) + '...'
    }

    // maxLength=5 means slice(0,5), so first 5 chars '这是一个很' + '...' = 8 chars
    expect(truncateText('这是一个很长的文本', 5)).toBe('这是一个很...')
    expect(truncateText('短文本', 10)).toBe('短文本')
  })
})

// =============================================================================
// Interaction Behavior Tests
// =============================================================================

describe('Interaction Behavior', () => {
  it('shows confirmation before destructive actions', async () => {
    const user = userEvent.setup()
    const handleDelete = vi.fn()

    const TestDeleteButton = ({ onDelete }: { onDelete: () => void }) => (
      <button onClick={() => {
        if (window.confirm('确定要删除吗？')) {
          onDelete()
        }
      }}>
        删除
      </button>
    )

    vi.stubGlobal('confirm', vi.fn(() => true))

    render(<TestDeleteButton onDelete={handleDelete} />)
    await user.click(screen.getByRole('button', { name: '删除' }))

    await waitFor(() => {
      expect(confirm).toHaveBeenCalledWith('确定要删除吗？')
    })
  })

  it('debounces search input', () => {
    vi.useFakeTimers()

    const handleSearch = vi.fn()

    let timeoutId: ReturnType<typeof setTimeout>
    const TestSearchInput = ({ onSearch }: { onSearch: (value: string) => void }) => {
      return (
        <input
          onChange={(e) => {
            clearTimeout(timeoutId)
            timeoutId = setTimeout(() => onSearch(e.target.value), 300)
          }}
        />
      )
    }

    render(<TestSearchInput onSearch={handleSearch} />)
    const input = screen.getByRole('textbox') as HTMLInputElement

    // Use fireEvent.change to trigger the onChange handler
    fireEvent.change(input, { target: { value: 'test' } })

    // Advance timers to trigger debounced callback
    vi.advanceTimersByTime(300)

    expect(handleSearch).toHaveBeenCalledWith('test')
    vi.useRealTimers()
  })

  it('restricts date picker to valid range', async () => {
    const validateDateRange = (date: Date, min: Date, max: Date) => {
      return date >= min && date <= max
    }

    const min = new Date('2024-01-01')
    const max = new Date('2024-12-31')

    expect(validateDateRange(new Date('2024-06-15'), min, max)).toBe(true)
    expect(validateDateRange(new Date('2023-06-15'), min, max)).toBe(false)
    expect(validateDateRange(new Date('2025-06-15'), min, max)).toBe(false)
  })

  it('auto-selects text on input focus', () => {
    const TestInput = () => (
      <input defaultValue="可编辑内容" />
    )

    render(<TestInput />)
    const input = screen.getByRole('textbox') as HTMLInputElement

    // Verify input renders with correct value
    expect(input).toHaveValue('可编辑内容')
  })
})

// =============================================================================
// Helper Components for Testing
// =============================================================================

function PageHeaderTest({
  title,
  description,
  breadcrumb,
  actions,
  footer,
}: {
  title: string
  description?: string
  breadcrumb?: Array<{ title: string; path?: string }>
  actions?: ReactNode
  footer?: ReactNode
}) {
  return (
    <div data-testid="page-header">
      {breadcrumb && <div data-testid="breadcrumb">{breadcrumb.map(b => b.title).join(' > ')}</div>}
      <h1 data-testid="title">{title}</h1>
      {description && <p data-testid="description">{description}</p>}
      {actions && <div data-testid="actions">{actions}</div>}
      {footer && <div data-testid="footer">{footer}</div>}
    </div>
  )
}
