import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LoginForm } from './LoginForm'

const socialProviders = [
  { label: 'Continue with Google', onClick: vi.fn() },
  { label: 'Continue with GitHub', onClick: vi.fn() },
]

const allProps = {
  logo: <div data-testid="logo">Logo</div>,
  title: 'Welcome back',
  socialProviders,
  onSubmit: vi.fn(),
  onForgotPassword: vi.fn(),
  onSignUp: vi.fn(),
}

describe('LoginForm', () => {
  describe('rendering', () => {
    it('renders all elements when all props provided', () => {
      render(<LoginForm {...allProps} />)
      expect(screen.getByTestId('logo')).toBeInTheDocument()
      expect(screen.getByText('Welcome back')).toBeInTheDocument()
      expect(screen.getByText('Continue with Google')).toBeInTheDocument()
      expect(screen.getByText('Continue with GitHub')).toBeInTheDocument()
      expect(screen.getByText('or')).toBeInTheDocument()
      expect(screen.getByLabelText(/email/i)).toBeInTheDocument()
      expect(screen.getByLabelText(/password/i)).toBeInTheDocument()
      expect(screen.getByLabelText(/remember me/i)).toBeInTheDocument()
      expect(screen.getByText(/forgot password/i)).toBeInTheDocument()
      expect(screen.getByRole('button', { name: 'Sign in' })).toBeInTheDocument()
      expect(screen.getByText(/sign up/i)).toBeInTheDocument()
    })

    it('renders default title when title prop omitted', () => {
      render(<LoginForm onSubmit={vi.fn()} />)
      expect(screen.getByRole('heading', { name: 'Sign in' })).toBeInTheDocument()
    })

    it('hides social section when socialProviders is empty', () => {
      render(<LoginForm onSubmit={vi.fn()} socialProviders={[]} />)
      expect(screen.queryByText('or')).not.toBeInTheDocument()
    })

    it('hides social section when socialProviders is omitted', () => {
      render(<LoginForm onSubmit={vi.fn()} />)
      expect(screen.queryByText('or')).not.toBeInTheDocument()
    })

    it('hides forgot password link when onForgotPassword omitted', () => {
      render(<LoginForm onSubmit={vi.fn()} />)
      expect(screen.queryByText(/forgot password/i)).not.toBeInTheDocument()
    })

    it('hides sign up link when onSignUp omitted', () => {
      render(<LoginForm onSubmit={vi.fn()} />)
      expect(screen.queryByText(/sign up/i)).not.toBeInTheDocument()
    })

    it('hides credentials section when onSubmit omitted (social only)', () => {
      render(<LoginForm socialProviders={socialProviders} />)
      expect(screen.queryByLabelText(/email/i)).not.toBeInTheDocument()
      expect(screen.queryByLabelText(/password/i)).not.toBeInTheDocument()
      expect(screen.queryByRole('button', { name: 'Sign in' })).not.toBeInTheDocument()
      expect(screen.queryByText('or')).not.toBeInTheDocument()
      // Social buttons should still render
      expect(screen.getByText('Continue with Google')).toBeInTheDocument()
    })

    it('shows server error Alert when error prop set', () => {
      render(<LoginForm onSubmit={vi.fn()} error="Invalid credentials" />)
      expect(screen.getByText('Invalid credentials')).toBeInTheDocument()
    })
  })

  describe('validation', () => {
    it('validates required email', async () => {
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} />)
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(screen.getByText('Email is required')).toBeInTheDocument()
    })

    it('validates email format', async () => {
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} />)
      await user.type(screen.getByLabelText(/email/i), 'notanemail')
      await user.type(screen.getByLabelText(/password/i), 'password123')
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(screen.getByText('Please enter a valid email address')).toBeInTheDocument()
    })

    it('validates required password', async () => {
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} />)
      await user.type(screen.getByLabelText(/email/i), 'test@example.com')
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(screen.getByText('Password is required')).toBeInTheDocument()
    })

    it('validates password minimum length', async () => {
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} />)
      await user.type(screen.getByLabelText(/email/i), 'test@example.com')
      await user.type(screen.getByLabelText(/password/i), 'short')
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(screen.getByText('Password must be at least 8 characters')).toBeInTheDocument()
    })

    it('clears field errors on typing', async () => {
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} />)
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(screen.getByText('Email is required')).toBeInTheDocument()
      await user.type(screen.getByLabelText(/email/i), 't')
      expect(screen.queryByText('Email is required')).not.toBeInTheDocument()
    })

    it('calls onSubmit with credentials when valid', async () => {
      const onSubmit = vi.fn()
      const user = userEvent.setup()
      render(<LoginForm onSubmit={onSubmit} />)
      await user.type(screen.getByLabelText(/email/i), 'test@example.com')
      await user.type(screen.getByLabelText(/password/i), 'password123')
      await user.click(screen.getByLabelText(/remember me/i))
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(onSubmit).toHaveBeenCalledWith({
        email: 'test@example.com',
        password: 'password123',
        remember: true,
      })
    })

    it('does not call onSubmit when validation fails', async () => {
      const onSubmit = vi.fn()
      const user = userEvent.setup()
      render(<LoginForm onSubmit={onSubmit} />)
      await user.click(screen.getByRole('button', { name: 'Sign in' }))
      expect(onSubmit).not.toHaveBeenCalled()
    })
  })

  describe('loading state', () => {
    it('disables form inputs when loading', () => {
      render(<LoginForm {...allProps} loading />)
      expect(screen.getByLabelText(/email/i)).toBeDisabled()
      expect(screen.getByLabelText(/password/i)).toBeDisabled()
      expect(screen.getByLabelText(/remember me/i)).toBeDisabled()
    })

    it('shows spinner on submit button when loading', () => {
      render(<LoginForm {...allProps} loading />)
      const submitBtn = screen.getByRole('button', { name: /sign in/i })
      expect(submitBtn).toBeDisabled()
      // Button component renders Spinner when loading=true
      expect(submitBtn.querySelector('[class*="spinner"]')).toBeInTheDocument()
    })

    it('disables social buttons when loading', () => {
      render(<LoginForm {...allProps} loading />)
      expect(screen.getByRole('button', { name: 'Continue with Google' })).toBeDisabled()
      expect(screen.getByRole('button', { name: 'Continue with GitHub' })).toBeDisabled()
    })
  })

  describe('callbacks', () => {
    it('calls social provider onClick when clicked', async () => {
      const onClick = vi.fn()
      const user = userEvent.setup()
      render(<LoginForm socialProviders={[{ label: 'Continue with Google', onClick }]} onSubmit={vi.fn()} />)
      await user.click(screen.getByRole('button', { name: 'Continue with Google' }))
      expect(onClick).toHaveBeenCalledOnce()
    })

    it('calls onForgotPassword when link clicked', async () => {
      const onForgotPassword = vi.fn()
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} onForgotPassword={onForgotPassword} />)
      await user.click(screen.getByText(/forgot password/i))
      expect(onForgotPassword).toHaveBeenCalledOnce()
    })

    it('calls onSignUp when link clicked', async () => {
      const onSignUp = vi.fn()
      const user = userEvent.setup()
      render(<LoginForm onSubmit={vi.fn()} onSignUp={onSignUp} />)
      await user.click(screen.getByText(/sign up/i))
      expect(onSignUp).toHaveBeenCalledOnce()
    })
  })
})
