import type { MockedFunction } from 'vite-plus/test'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { useLocale } from '@/context/i18n'
import { useSearchParams } from '@/next/navigation'
import { useSendMail } from '@/service/use-common'
import { renderWithConsoleQuery } from '@/test/console/query-data'
import SignupEmailForm from './input-mail'

const mockSubmitMail = vi.fn()
const mockOnSuccess = vi.fn()

vi.mock('@/next/link', () => ({
  default: ({
    children,
    href,
    className,
    target,
    rel,
  }: {
    children: React.ReactNode
    href: string
    className?: string
    target?: string
    rel?: string
  }) => (
    <a href={href} className={className} target={target} rel={rel}>
      {children}
    </a>
  ),
}))

vi.mock('@/context/i18n', () => ({
  useLocale: vi.fn(),
}))

vi.mock('@/next/navigation', () => ({
  useSearchParams: vi.fn(),
}))

vi.mock('@/service/use-common', () => ({
  useSendMail: vi.fn(),
}))

type UseSendMailResult = ReturnType<typeof useSendMail>

const mockUseLocale = useLocale as unknown as MockedFunction<typeof useLocale>
const mockUseSearchParams = useSearchParams as unknown as MockedFunction<typeof useSearchParams>
const mockUseSendMail = useSendMail as unknown as MockedFunction<typeof useSendMail>

const renderForm = ({
  brandingEnabled = false,
  isPending = false,
}: {
  brandingEnabled?: boolean
  isPending?: boolean
} = {}) => {
  mockUseLocale.mockReturnValue('en-US')
  mockUseSendMail.mockReturnValue({
    mutateAsync: mockSubmitMail,
    isPending,
  } as unknown as UseSendMailResult)
  return renderWithConsoleQuery(<SignupEmailForm onSuccess={mockOnSuccess} />, {
    systemFeatures: { branding: { enabled: brandingEnabled } },
  })
}

describe('InputMail Form', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockUseSearchParams.mockReturnValue(
      new URLSearchParams() as unknown as ReturnType<typeof useSearchParams>,
    )
    mockSubmitMail.mockResolvedValue({ result: 'success', data: 'token' })
  })

  // Rendering baseline UI elements.
  describe('Rendering', () => {
    it('should render email input and submit button', () => {
      renderForm()

      const emailInput = screen.getByRole('textbox', { name: 'login.email' })
      const submitButton = screen.getByRole('button', { name: 'login.signup.verifyMail' })

      expect(emailInput).toHaveAttribute('name', 'email')
      expect(emailInput).toHaveAttribute('type', 'email')
      expect(emailInput).toHaveAttribute('autocomplete', 'email')
      expect(emailInput).toHaveAttribute('spellcheck', 'false')
      expect(emailInput).toHaveProperty('tabIndex', 0)
      expect(submitButton).toHaveAttribute('type', 'submit')
      expect(submitButton).toHaveProperty('tabIndex', 0)
      expect(screen.getByRole('link', { name: 'login.signup.signIn' })).toBeInTheDocument()
    })
  })

  // Prop-driven branding content visibility.
  describe('Props', () => {
    it('should show terms links when branding is disabled', () => {
      renderForm({ brandingEnabled: false })

      expect(screen.getByRole('link', { name: 'login.tos' })).toBeInTheDocument()
      expect(screen.getByRole('link', { name: 'login.pp' })).toBeInTheDocument()
    })

    it('should hide terms links when branding is enabled', () => {
      renderForm({ brandingEnabled: true })

      expect(screen.queryByRole('link', { name: 'login.tos' })).not.toBeInTheDocument()
      expect(screen.queryByRole('link', { name: 'login.pp' })).not.toBeInTheDocument()
    })
  })

  // Submission flow and mutation integration.
  describe('User Interactions', () => {
    it('should submit email and call onSuccess when mutation succeeds', async () => {
      const user = userEvent.setup()
      renderForm()
      const input = screen.getByRole('textbox', { name: 'login.email' })

      await user.type(input, 'test@example.com{Enter}')

      expect(mockSubmitMail).toHaveBeenCalledWith({
        email: 'test@example.com',
        language: 'en-US',
      })
      expect(mockSubmitMail).toHaveBeenCalledTimes(1)

      await waitFor(() => {
        expect(mockOnSuccess).toHaveBeenCalledWith('test@example.com', 'token')
      })
    })
  })

  // Navigation between registration and login keeps the original destination.
  describe('Registration Navigation', () => {
    it('should preserve the current query when navigating to sign in', () => {
      mockUseSearchParams.mockReturnValue(
        new URLSearchParams(
          'redirect_url=%2Fapps%3Ftag%3Dworkflow&source=pricing',
        ) as unknown as ReturnType<typeof useSearchParams>,
      )

      renderForm()

      expect(screen.getByRole('link', { name: 'login.signup.signIn' })).toHaveAttribute(
        'href',
        '/signin?redirect_url=%2Fapps%3Ftag%3Dworkflow&source=pricing',
      )
    })
  })

  // Validation and failure paths.
  describe('Edge Cases', () => {
    it('should block submission when email is invalid', async () => {
      const user = userEvent.setup()
      renderForm()
      const input = screen.getByLabelText('login.email')

      await user.type(input, 'invalid-email{Enter}')

      const error = await screen.findByText('login.error.emailInValid')
      expect(input).toHaveAttribute('aria-invalid', 'true')
      expect(input).toHaveAccessibleDescription(error.textContent ?? '')
      expect(input).toHaveFocus()
      expect(mockSubmitMail).not.toHaveBeenCalled()
      expect(mockOnSuccess).not.toHaveBeenCalled()
    })

    it('should not call onSuccess when mutation does not succeed', async () => {
      const user = userEvent.setup()
      mockSubmitMail.mockResolvedValue({ result: 'failed', data: 'token' })
      renderForm()
      const input = screen.getByLabelText('login.email')
      const button = screen.getByRole('button', { name: 'login.signup.verifyMail' })

      await user.type(input, 'test@example.com')
      await user.click(button)

      await waitFor(() => {
        expect(mockSubmitMail).toHaveBeenCalled()
      })
      expect(mockOnSuccess).not.toHaveBeenCalled()
    })
  })
})
