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

// Mock next/navigation
const mockPush = vi.fn()
const mockRefresh = vi.fn()

vi.mock('next/navigation', () => ({
  useRouter: () => ({
    push: mockPush,
    refresh: mockRefresh,
  }),
}))

// Mock sonner toast
const mockToastSuccess = vi.fn()
vi.mock('sonner', () => ({
  toast: {
    success: (...args: any[]) => mockToastSuccess(...args),
  },
}))

// Mock supabase
const mockSignInWithPassword = vi.fn()
const mockSignOut = vi.fn()
const mockFrom = vi.fn()

vi.mock('@/lib/supabase', () => ({
  supabase: {
    auth: {
      signInWithPassword: (...args: any[]) => mockSignInWithPassword(...args),
      signOut: () => mockSignOut(),
    },
    from: (...args: any[]) => mockFrom(...args),
  },
}))

describe('LoginPage', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('should render login form', () => {
    render(<LoginPage />)

    expect(screen.getByRole('heading', { name: /tabatafit admin/i })).toBeInTheDocument()
    expect(screen.getByLabelText(/email/i)).toBeInTheDocument()
    expect(screen.getByLabelText(/password/i)).toBeInTheDocument()
    expect(screen.getByRole('button', { name: /sign in/i })).toBeInTheDocument()
  })

  it('should update form fields on input', async () => {
    render(<LoginPage />)

    const emailInput = screen.getByLabelText(/email/i)
    const passwordInput = screen.getByLabelText(/password/i)

    await userEvent.type(emailInput, 'admin@example.com')
    await userEvent.type(passwordInput, 'password123')

    expect(emailInput).toHaveValue('admin@example.com')
    expect(passwordInput).toHaveValue('password123')
  })

  it('should show validation errors for empty fields', async () => {
    render(<LoginPage />)

    const submitButton = screen.getByRole('button', { name: /sign in/i })
    
    // HTML5 validation should prevent submission
    expect(screen.getByLabelText(/email/i)).toBeRequired()
    expect(screen.getByLabelText(/password/i)).toBeRequired()
  })

  it('should handle successful login', async () => {
    const mockUser = { id: 'user-123', email: 'admin@example.com' }
    mockSignInWithPassword.mockResolvedValue({ 
      data: { user: mockUser }, 
      error: null 
    })

    mockFrom.mockReturnValue({
      select: () => ({
        eq: () => ({
          single: () => Promise.resolve({ 
            data: { id: 'user-123', email: 'admin@example.com', role: 'admin' }, 
            error: null 
          }),
        }),
      }),
    })

    render(<LoginPage />)

    await userEvent.type(screen.getByLabelText(/email/i), 'admin@example.com')
    await userEvent.type(screen.getByLabelText(/password/i), 'password123')
    await userEvent.click(screen.getByRole('button', { name: /sign in/i }))

    await waitFor(() => {
      expect(mockSignInWithPassword).toHaveBeenCalledWith({
        email: 'admin@example.com',
        password: 'password123',
      })
    })

    await waitFor(() => {
      expect(mockToastSuccess).toHaveBeenCalledWith('Welcome back!', {
        description: 'You have successfully signed in.',
      })
    })
  })

  it('should show error for invalid credentials', async () => {
    mockSignInWithPassword.mockResolvedValue({ 
      data: { user: null }, 
      error: { message: 'Invalid login credentials' } 
    })

    render(<LoginPage />)

    await userEvent.type(screen.getByLabelText(/email/i), 'wrong@example.com')
    await userEvent.type(screen.getByLabelText(/password/i), 'wrongpassword')
    await userEvent.click(screen.getByRole('button', { name: /sign in/i }))

    await waitFor(() => {
      expect(screen.getByText(/invalid login credentials/i)).toBeInTheDocument()
    })
  })

  it('should handle non-admin user', async () => {
    const mockUser = { id: 'user-123', email: 'user@example.com' }
    mockSignInWithPassword.mockResolvedValue({ 
      data: { user: mockUser }, 
      error: null 
    })

    mockFrom.mockReturnValue({
      select: () => ({
        eq: () => ({
          single: () => Promise.resolve({ data: null, error: null }),
        }),
      }),
    })

    mockSignOut.mockResolvedValue({ error: null })

    render(<LoginPage />)

    await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com')
    await userEvent.type(screen.getByLabelText(/password/i), 'password123')
    await userEvent.click(screen.getByRole('button', { name: /sign in/i }))

    await waitFor(() => {
      expect(screen.getByText(/not authorized as admin/i)).toBeInTheDocument()
    })

    expect(mockSignOut).toHaveBeenCalled()
  })

  it('should show loading state during login', async () => {
    mockSignInWithPassword.mockImplementation(() => new Promise(() => {})) // Never resolves

    render(<LoginPage />)

    await userEvent.type(screen.getByLabelText(/email/i), 'admin@example.com')
    await userEvent.type(screen.getByLabelText(/password/i), 'password123')
    await userEvent.click(screen.getByRole('button', { name: /sign in/i }))

    await waitFor(() => {
      expect(screen.getByText(/signing in/i)).toBeInTheDocument()
    })

    const button = screen.getByRole('button', { name: /signing in/i })
    expect(button).toBeDisabled()
  })

  it('should have proper input types', () => {
    render(<LoginPage />)

    expect(screen.getByLabelText(/email/i)).toHaveAttribute('type', 'email')
    expect(screen.getByLabelText(/password/i)).toHaveAttribute('type', 'password')
  })

  it('should render logo and branding', () => {
    render(<LoginPage />)

    expect(screen.getByText('TabataFit Admin')).toBeInTheDocument()
    expect(screen.getByText(/sign in to manage your content/i)).toBeInTheDocument()
  })

  it('should handle network errors', async () => {
    mockSignInWithPassword.mockRejectedValue(new Error('Network error'))

    render(<LoginPage />)

    await userEvent.type(screen.getByLabelText(/email/i), 'admin@example.com')
    await userEvent.type(screen.getByLabelText(/password/i), 'password123')
    await userEvent.click(screen.getByRole('button', { name: /sign in/i }))

    await waitFor(() => {
      expect(screen.getByText(/network error/i)).toBeInTheDocument()
    })
  })
})