/**
 * Tests de la state machine useSimulation.
 *
 * Transitions couvertes :
 *   idle → task-selected (selectTask success)
 *   task-selected → correcting (submitText déclenché)
 *   correcting → done (correctEe success)
 *   correcting → task-selected (correctEe error)
 *   * → idle (reset)
 *   guard submitText sans production (aucune mutation)
 */

import { renderHook, act, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useSimulation } from '../useSimulation'
import { createSimulation } from '@/entities/production/api'
import { correctEe } from '@/entities/report/api'
import type { Production } from '@/entities/production/types'
import type { Report } from '@/entities/report/types'

vi.mock('@/entities/production/api')
vi.mock('@/entities/report/api')

const mockCreateSimulation = vi.mocked(createSimulation)
const mockCorrectEe = vi.mocked(correctEe)

const mockProduction: Production = {
  id: 'sim-1',
  tache: 'EE_T1',
  mode: 'entrainement',
  created_at: '2026-04-19T00:00:00Z',
  sujet: null,
}

const mockReport: Report = {
  simulation_id: 'sim-1',
  score: 80,
  nclc: 9,
  feedback_court: 'Bon travail.',
  criteres: [],
  erreurs: [],
  modele: '',
  idees: [],
  exercices: [],
}

function createWrapper() {
  const queryClient = new QueryClient({
    defaultOptions: { mutations: { retry: false } },
  })
  return function Wrapper({ children }: { children: React.ReactNode }) {
    return React.createElement(QueryClientProvider, { client: queryClient }, children)
  }
}

beforeEach(() => {
  vi.clearAllMocks()
})

describe('useSimulation — état initial', () => {
  it('step = idle, production null, report null', () => {
    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })
    expect(result.current.step).toBe('idle')
    expect(result.current.production).toBeNull()
    expect(result.current.report).toBeNull()
  })
})

describe('useSimulation — selectTask', () => {
  it('step passe à task-selected et production est hydratée après succès', async () => {
    mockCreateSimulation.mockResolvedValue(mockProduction)

    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => {
      result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' })
    })

    await waitFor(() => expect(result.current.step).toBe('task-selected'))
    expect(result.current.production).toEqual(mockProduction)
  })

  it('isCreating = true pendant la mutation createSimulation', async () => {
    let resolveCreate!: (p: Production) => void
    mockCreateSimulation.mockImplementation(() => new Promise(r => { resolveCreate = r }))

    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => {
      result.current.selectTask({ tache: 'EE_T2', mode: 'entrainement' })
    })

    await waitFor(() => expect(result.current.isCreating).toBe(true))

    act(() => resolveCreate(mockProduction))
    await waitFor(() => expect(result.current.isCreating).toBe(false))
  })
})

describe('useSimulation — submitText', () => {
  it('step correcting pendant la correction, puis done après succès', async () => {
    mockCreateSimulation.mockResolvedValue(mockProduction)

    let resolveCorrect!: (r: Report) => void
    mockCorrectEe.mockImplementation(() => new Promise(r => { resolveCorrect = r }))

    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' }))
    await waitFor(() => expect(result.current.step).toBe('task-selected'))

    act(() => result.current.submitText('Mon texte de production.'))
    await waitFor(() => expect(result.current.step).toBe('correcting'))

    act(() => resolveCorrect(mockReport))
    await waitFor(() => expect(result.current.step).toBe('done'))
    expect(result.current.report).toEqual(mockReport)
  })

  it('step revient à task-selected si correctEe échoue', async () => {
    mockCreateSimulation.mockResolvedValue(mockProduction)
    mockCorrectEe.mockRejectedValue({ code: 'SIMULATION_NOT_FOUND', message: 'Not found' })

    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' }))
    await waitFor(() => expect(result.current.step).toBe('task-selected'))

    act(() => result.current.submitText('Mon texte.')
    )
    await waitFor(() => expect(result.current.step).toBe('task-selected'))
    expect(result.current.report).toBeNull()
  })

  it('submitText sans production ne déclenche aucune mutation', async () => {
    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => result.current.submitText('texte quelconque'))

    expect(mockCorrectEe).not.toHaveBeenCalled()
    expect(result.current.step).toBe('idle')
  })
})

describe('useSimulation — reset', () => {
  it('reset depuis task-selected remet step à idle et production à null', async () => {
    mockCreateSimulation.mockResolvedValue(mockProduction)

    const { result } = renderHook(() => useSimulation(), { wrapper: createWrapper() })

    act(() => result.current.selectTask({ tache: 'EE_T1', mode: 'entrainement' }))
    await waitFor(() => expect(result.current.step).toBe('task-selected'))

    act(() => result.current.reset())

    expect(result.current.step).toBe('idle')
    expect(result.current.production).toBeNull()
  })
})
