import React from 'react'
import { render, screen, waitFor } from '../../test-utils'
import { AuthProvider, useAuthContext } from '../AuthContext'
import { UserRole } from '../../types/auth'
import authService from '../../services/auth'

// Mock du service d'authentification
jest.mock('../../services/auth')

const mockAuthService = authService as jest.Mocked<typeof authService>

// Composant de test pour utiliser le contexte
const TestComponent = () => {
  const auth = useAuthContext()
  
  return (
    <div>
      <div data-testid="loading">{auth.loading ? 'Loading' : 'Not Loading'}</div>
      <div data-testid="authenticated">{auth.isAuthenticated ? 'Authenticated' : 'Not Authenticated'}</div>
      <div data-testid="user">{auth.user ? `User: ${auth.user.email}` : 'No User'}</div>
      <div data-testid="error">{auth.error || 'No Error'}</div>
      <button onClick={() => auth.login({ email: 'test@test.com', password: 'password' })}>
        Login
      </button>
      <button onClick={() => auth.logout()}>Logout</button>
      <button onClick={() => auth.clearError()}>Clear Error</button>
    </div>
  )
}

describe('AuthContext', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    mockAuthService.getToken.mockReturnValue(null)
    mockAuthService.getCurrentUserFromStorage.mockReturnValue(null)
  })

  it('devrait initialiser avec un état par défaut', async () => {
    render(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    )

    await waitFor(() => {
      expect(screen.getByTestId('loading')).toHaveTextContent('Not Loading')
      expect(screen.getByTestId('authenticated')).toHaveTextContent('Not Authenticated')
      expect(screen.getByTestId('user')).toHaveTextContent('No User')
      expect(screen.getByTestId('error')).toHaveTextContent('No Error')
    })
  })

  it('devrait initialiser avec un utilisateur connecté si token valide', async () => {
    const mockUser = {
      id: '1',
      email: 'test@test.com',
      nom: 'Test',
      prenom: 'User',
      role: UserRole.ADMIN,
      telephone: '0612345678',
      adresse: '123 rue Test',
      codePostal: '75001',
      ville: 'Paris'
    }

    mockAuthService.getToken.mockReturnValue('valid-token')
    mockAuthService.getCurrentUserFromStorage.mockReturnValue(mockUser)
    mockAuthService.getCurrentUser.mockResolvedValue(mockUser)

    render(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    )

    await waitFor(() => {
      expect(screen.getByTestId('authenticated')).toHaveTextContent('Authenticated')
      expect(screen.getByTestId('user')).toHaveTextContent('User: test@test.com')
    })
  })

  it('devrait nettoyer l\'état si le token est invalide', async () => {
    const mockUser = {
      id: '1',
      email: 'test@test.com',
      nom: 'Test',
      prenom: 'User',
      role: UserRole.ADMIN,
      telephone: '0612345678',
      adresse: '123 rue Test',
      codePostal: '75001',
      ville: 'Paris'
    }

    mockAuthService.getToken.mockReturnValue('invalid-token')
    mockAuthService.getCurrentUserFromStorage.mockReturnValue(mockUser)
    mockAuthService.getCurrentUser.mockResolvedValue(null)
    mockAuthService.logout.mockResolvedValue()

    render(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    )

    await waitFor(() => {
      expect(screen.getByTestId('authenticated')).toHaveTextContent('Not Authenticated')
      expect(screen.getByTestId('user')).toHaveTextContent('No User')
    })

    expect(mockAuthService.logout).toHaveBeenCalled()
  })

  it('devrait gérer les erreurs d\'initialisation', async () => {
    mockAuthService.getToken.mockImplementation(() => {
      throw new Error('Storage error')
    })

    render(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    )

    await waitFor(() => {
      expect(screen.getByTestId('error')).toHaveTextContent('Erreur d\'initialisation')
      expect(screen.getByTestId('authenticated')).toHaveTextContent('Not Authenticated')
    })
  })

  it('devrait lancer une erreur si utilisé hors du provider', () => {
    // Supprimer les erreurs de console pour ce test
    const originalError = console.error
    console.error = jest.fn()

    expect(() => {
      render(<TestComponent />)
    }).toThrow('useAuthContext must be used within an AuthProvider')

    console.error = originalError
  })

  it('devrait permettre d\'effacer les erreurs', async () => {
    mockAuthService.getToken.mockImplementation(() => {
      throw new Error('Storage error')
    })

    render(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    )

    await waitFor(() => {
      expect(screen.getByTestId('error')).toHaveTextContent('Erreur d\'initialisation')
    })

    // Effacer l'erreur
    screen.getByText('Clear Error').click()

    await waitFor(() => {
      expect(screen.getByTestId('error')).toHaveTextContent('No Error')
    })
  })
})

describe('AuthContext avec useAuth mock', () => {
  const mockUseAuth = {
    user: null,
    token: null,
    isAuthenticated: false,
    loading: false,
    error: null,
    login: jest.fn(),
    register: jest.fn(),
    logout: jest.fn(),
    clearError: jest.fn(),
    hasPermission: jest.fn(),
    hasRole: jest.fn(),
    hasAnyRole: jest.fn(),
  }

  beforeEach(() => {
    jest.clearAllMocks()
    // Mock direct du hook useAuth
    jest.doMock('../../hooks/useAuth', () => ({
      __esModule: true,
      default: jest.fn(() => mockUseAuth),
    }))
  })

  afterEach(() => {
    jest.dontMock('../../hooks/useAuth')
  })

  it('devrait fournir toutes les méthodes du contexte', () => {
    const TestHookComponent = () => {
      const auth = useAuthContext()
      
      return (
        <div>
          <div data-testid="methods-available">
            {typeof auth.login === 'function' &&
             typeof auth.register === 'function' &&
             typeof auth.logout === 'function' &&
             typeof auth.clearError === 'function' &&
             typeof auth.hasPermission === 'function' &&
             typeof auth.hasRole === 'function' &&
             typeof auth.hasAnyRole === 'function' ? 'All methods available' : 'Missing methods'}
          </div>
        </div>
      )
    }

    render(
      <AuthProvider>
        <TestHookComponent />
      </AuthProvider>
    )

    expect(screen.getByTestId('methods-available')).toHaveTextContent('All methods available')
  })
})