import { renderHook, waitFor } from '@testing-library/react'
import { useDashboard } from '../useDashboard'
import { clientService, chantierService, devisService, factureService } from '../../services/api'

// Mock des services
jest.mock('../../services/api')

const mockClientService = clientService as jest.Mocked<typeof clientService>
const mockChantierService = chantierService as jest.Mocked<typeof chantierService>
const mockDevisService = devisService as jest.Mocked<typeof devisService>
const mockFactureService = factureService as jest.Mocked<typeof factureService>

const mockClients = [
  { id: '1', nom: 'Client', prenom: 'Un', email: 'client1@test.com' },
  { id: '2', nom: 'Client', prenom: 'Deux', email: 'client2@test.com' },
]

const mockChantiers = [
  {
    id: '1',
    nom: 'Chantier 1',
    client: mockClients[0],
    statut: 'EN_COURS',
    dateDebut: '2024-01-01',
    montantPrevu: 10000,
  },
  {
    id: '2',
    nom: 'Chantier 2',
    client: mockClients[1],
    statut: 'PLANIFIE',
    dateDebut: '2024-01-15',
    montantPrevu: 15000,
  },
  {
    id: '3',
    nom: 'Chantier 3',
    client: mockClients[0],
    statut: 'TERMINE',
    dateDebut: '2024-01-10',
    montantPrevu: 8000,
  },
]

const mockDevis = [
  {
    id: '1',
    numero: 'D2024-001',
    client: mockClients[0],
    statut: 'ENVOYE',
    dateEmission: '2024-01-01',
    dateValidite: '2024-12-31',
    montantTTC: 5000,
  },
  {
    id: '2',
    numero: 'D2024-002',
    client: mockClients[1],
    statut: 'ACCEPTE',
    dateEmission: '2024-01-05',
    dateValidite: '2024-12-31',
    montantTTC: 7000,
  },
]

const mockFactures = [
  {
    id: '1',
    numero: 'F2024-001',
    client: mockClients[0],
    statut: 'PAYEE',
    dateEmission: '2024-01-01',
    dateEcheance: '2024-01-31',
    montantTTC: 10000,
  },
  {
    id: '2',
    numero: 'F2024-002',
    client: mockClients[1],
    statut: 'ENVOYEE',
    dateEmission: '2024-01-10',
    dateEcheance: '2024-01-01', // En retard
    montantTTC: 5000,
  },
]

describe('Hook useDashboard', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    jest.spyOn(console, 'log').mockImplementation(() => {})
    jest.spyOn(console, 'error').mockImplementation(() => {})
  })

  afterEach(() => {
    jest.restoreAllMocks()
  })

  it('devrait initialiser avec les valeurs par défaut', () => {
    mockClientService.getAll.mockImplementation(() => new Promise(() => {}))
    mockChantierService.getAll.mockImplementation(() => new Promise(() => {}))
    mockDevisService.getAll.mockImplementation(() => new Promise(() => {}))
    mockFactureService.getAll.mockImplementation(() => new Promise(() => {}))

    const { result } = renderHook(() => useDashboard())

    expect(result.current.stats).toBeNull()
    expect(result.current.chantiersRecents).toEqual([])
    expect(result.current.facturesEnRetard).toEqual([])
    expect(result.current.devisEnAttente).toEqual([])
    expect(result.current.loading).toBe(true)
    expect(result.current.error).toBeNull()
  })

  it('devrait charger les données avec succès', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.stats).toEqual({
      totalClients: 2,
      totalChantiers: 3,
      chantiersEnCours: 1,
      chantiersPlanifies: 1,
      chantiersTermines: 1,
      totalDevis: 2,
      devisAcceptes: 1,
      devisEnAttente: 1,
      totalFactures: 2,
      facturesPayees: 1,
      facturesEnRetard: 1,
      chiffreAffaires: 10000,
      chiffreAffairesMois: expect.any(Number),
      chiffreAffairesAnnee: expect.any(Number),
    })
  })

  it('devrait calculer les chantiers récents', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.chantiersRecents).toHaveLength(3)
    expect(result.current.chantiersRecents[0]).toEqual({
      id: '2',
      nom: 'Chantier 2',
      client: 'Client Deux',
      statut: 'PLANIFIE',
      dateDebut: '2024-01-15',
      montantPrevu: 15000,
    })
  })

  it('devrait calculer les factures en retard', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.facturesEnRetard).toHaveLength(1)
    expect(result.current.facturesEnRetard[0]).toEqual({
      id: '2',
      numero: 'F2024-002',
      client: 'Client Deux',
      montantTTC: 5000,
      dateEcheance: '2024-01-01',
      joursRetard: expect.any(Number),
    })
  })

  it('devrait calculer les devis en attente', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.devisEnAttente).toHaveLength(1)
    expect(result.current.devisEnAttente[0]).toEqual({
      id: '1',
      numero: 'D2024-001',
      client: 'Client Un',
      montantTTC: 5000,
      dateEmission: '2024-01-01',
      dateValidite: '2024-12-31',
      joursRestants: expect.any(Number),
    })
  })

  it('devrait gérer les erreurs partielles', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockRejectedValue(new Error('Erreur chantiers'))
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.error).toBeNull()
    expect(result.current.stats?.totalClients).toBe(2)
    expect(result.current.stats?.totalChantiers).toBe(0)
    expect(result.current.chantiersRecents).toEqual([])
  })

  it('devrait gérer les erreurs complètes', async () => {
    mockClientService.getAll.mockRejectedValue(new Error('Erreur clients'))
    mockChantierService.getAll.mockRejectedValue(new Error('Erreur chantiers'))
    mockDevisService.getAll.mockRejectedValue(new Error('Erreur devis'))
    mockFactureService.getAll.mockRejectedValue(new Error('Erreur factures'))

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.error).toBeNull()
    expect(result.current.stats?.totalClients).toBe(0)
    expect(result.current.stats?.totalChantiers).toBe(0)
    expect(result.current.chantiersRecents).toEqual([])
  })

  it('devrait permettre de rafraîchir les données', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

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

    expect(mockClientService.getAll).toHaveBeenCalledTimes(1)

    // Rafraîchir
    result.current.refresh()

    await waitFor(() => {
      expect(mockClientService.getAll).toHaveBeenCalledTimes(2)
    })
  })

  it('devrait gérer les données non-array', async () => {
    mockClientService.getAll.mockResolvedValue(null as any)
    mockChantierService.getAll.mockResolvedValue(undefined as any)
    mockDevisService.getAll.mockResolvedValue({} as any)
    mockFactureService.getAll.mockResolvedValue('invalid' as any)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.stats?.totalClients).toBe(0)
    expect(result.current.stats?.totalChantiers).toBe(0)
    expect(result.current.stats?.totalDevis).toBe(0)
    expect(result.current.stats?.totalFactures).toBe(0)
  })

  it('devrait calculer correctement le chiffre d\'affaires mensuel', async () => {
    const today = new Date()
    const thisMonth = today.getMonth()
    const thisYear = today.getFullYear()
    
    const facturesThisMonth = [
      {
        id: '1',
        numero: 'F2024-001',
        client: mockClients[0],
        statut: 'PAYEE',
        dateEmission: new Date(thisYear, thisMonth, 1).toISOString(),
        dateEcheance: new Date(thisYear, thisMonth, 31).toISOString(),
        montantTTC: 10000,
      },
      {
        id: '2',
        numero: 'F2024-002',
        client: mockClients[1],
        statut: 'PAYEE',
        dateEmission: new Date(thisYear, thisMonth - 1, 1).toISOString(), // Mois précédent
        dateEcheance: new Date(thisYear, thisMonth - 1, 31).toISOString(),
        montantTTC: 5000,
      },
    ]

    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue([])
    mockDevisService.getAll.mockResolvedValue([])
    mockFactureService.getAll.mockResolvedValue(facturesThisMonth)

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.stats?.chiffreAffairesMois).toBe(10000)
  })

  it('devrait limiter les chantiers récents à 5', async () => {
    const manyChantiers = Array.from({ length: 10 }, (_, i) => ({
      id: `${i + 1}`,
      nom: `Chantier ${i + 1}`,
      client: mockClients[0],
      statut: 'EN_COURS',
      dateDebut: new Date(2024, 0, i + 1).toISOString(),
      montantPrevu: 10000,
    }))

    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(manyChantiers)
    mockDevisService.getAll.mockResolvedValue([])
    mockFactureService.getAll.mockResolvedValue([])

    const { result } = renderHook(() => useDashboard())

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

    expect(result.current.chantiersRecents).toHaveLength(5)
  })

  it('devrait fournir un alias isLoading', async () => {
    mockClientService.getAll.mockResolvedValue(mockClients)
    mockChantierService.getAll.mockResolvedValue(mockChantiers)
    mockDevisService.getAll.mockResolvedValue(mockDevis)
    mockFactureService.getAll.mockResolvedValue(mockFactures)

    const { result } = renderHook(() => useDashboard())

    expect(result.current.isLoading).toBe(result.current.loading)
  })
})