import React from 'react'
import { renderHook, act, waitFor } from '@/__tests__/utils/test-helpers'
import { AuthProvider, AuthContext } from './auth-provider'
import { useAuth } from '@/hooks/use-auth'
import { apiClient } from '@/lib/api-client'
import { mockLoginResponse } from '@/__tests__/fixtures/mock-data'

jest.mock('@/lib/api-client')
jest.mock('next/navigation', () => ({
  useRouter: () => ({
    push: jest.fn(),
    pathname: '/',
  }),
}))

describe('AuthProvider', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    localStorage.clear()
  })

  describe('initialization', () => {
    it('should initialize with no user when no token exists', async () => {
      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false)
      })

      expect(result.current.user).toBeNull()
      expect(result.current.isAuthenticated).toBe(false)
    })

    it('should load user from localStorage if token exists', async () => {
      const token = 'test-token'
      localStorage.setItem('auth_token', token)

      const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' }
      ;(apiClient.get as jest.Mock).mockResolvedValueOnce({
        success: true,
        data: mockUser,
      })

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false)
      })

      expect(result.current.token).toBe(token)
      expect(result.current.user).toEqual(mockUser)
    })

    it('should clear invalid token and set loading to false', async () => {
      localStorage.setItem('auth_token', 'invalid-token')
      ;(apiClient.get as jest.Mock).mockRejectedValueOnce(
        new Error('Unauthorized')
      )

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false)
      })

      expect(localStorage.getItem('auth_token')).toBeNull()
      expect(result.current.token).toBeNull()
    })
  })

  describe('login', () => {
    it('should successfully login user', async () => {
      const credentials = { email: 'test@example.com', password: 'password123' }
      ;(apiClient.post as jest.Mock).mockResolvedValueOnce(mockLoginResponse)

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await act(async () => {
        await result.current.login(credentials)
      })

      expect(result.current.token).toBe(mockLoginResponse.data.token)
      expect(result.current.user).toEqual(mockLoginResponse.data.user)
      expect(result.current.isAuthenticated).toBe(true)
      expect(localStorage.getItem('auth_token')).toBe(
        mockLoginResponse.data.token
      )
    })

    it('should throw error on login failure', async () => {
      const credentials = { email: 'test@example.com', password: 'wrong' }
      const errorMessage = 'Invalid credentials'
      ;(apiClient.post as jest.Mock).mockRejectedValueOnce(
        new Error(errorMessage)
      )

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await expect(
        act(async () => {
          await result.current.login(credentials)
        })
      ).rejects.toThrow(errorMessage)

      expect(result.current.isAuthenticated).toBe(false)
    })

    it('should handle failed login response', async () => {
      const credentials = { email: 'test@example.com', password: 'password' }
      ;(apiClient.post as jest.Mock).mockResolvedValueOnce({
        success: false,
        message: 'Login failed',
      })

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await expect(
        act(async () => {
          await result.current.login(credentials)
        })
      ).rejects.toThrow('Login failed')
    })
  })

  describe('register', () => {
    it('should successfully register user', async () => {
      const data = {
        name: 'New User',
        email: 'newuser@example.com',
        password: 'password123',
        password_confirmation: 'password123',
      }
      ;(apiClient.post as jest.Mock).mockResolvedValueOnce(mockLoginResponse)

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await act(async () => {
        await result.current.register(data)
      })

      expect(result.current.token).toBe(mockLoginResponse.data.token)
      expect(result.current.user).toEqual(mockLoginResponse.data.user)
      expect(result.current.isAuthenticated).toBe(true)
    })

    it('should throw error on registration failure', async () => {
      const data = {
        name: 'New User',
        email: 'newuser@example.com',
        password: 'password123',
        password_confirmation: 'password123',
      }
      const errorMessage = 'Email already exists'
      ;(apiClient.post as jest.Mock).mockRejectedValueOnce(
        new Error(errorMessage)
      )

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await expect(
        act(async () => {
          await result.current.register(data)
        })
      ).rejects.toThrow(errorMessage)

      expect(result.current.isAuthenticated).toBe(false)
    })
  })

  describe('logout', () => {
    it('should clear user and token on logout', async () => {
      const token = 'test-token'
      localStorage.setItem('auth_token', token)

      const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' }
      ;(apiClient.get as jest.Mock).mockResolvedValueOnce({
        success: true,
        data: mockUser,
      })

      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      await waitFor(() => {
        expect(result.current.isLoading).toBe(false)
      })

      act(() => {
        result.current.logout()
      })

      expect(result.current.token).toBeNull()
      expect(result.current.user).toBeNull()
      expect(result.current.isAuthenticated).toBe(false)
      expect(localStorage.getItem('auth_token')).toBeNull()
    })
  })

  describe('authentication state', () => {
    it('should correctly report isAuthenticated status', async () => {
      const wrapper = ({ children }: { children: React.ReactNode }) => (
        <AuthProvider>{children}</AuthProvider>
      )
      const { result } = renderHook(() => useAuth(), { wrapper })

      expect(result.current.isAuthenticated).toBe(false)

      localStorage.setItem('auth_token', 'test-token')
      const mockUser = { id: 1, name: 'Test', email: 'test@example.com', created_at: '', updated_at: '' }
      ;(apiClient.get as jest.Mock).mockResolvedValueOnce({
        success: true,
        data: mockUser,
      })

      await waitFor(() => {
        expect(result.current.isAuthenticated).toBe(true)
      })
    })
  })
})
