import { describe, it, expect, vi, beforeEach } from 'vitest'

import { useComposants } from '~/composables/useComposants'
import { mockComponentFromApi, wrapCollection } from '../fixtures/mockData'

// ---------------------------------------------------------------------------
// Mocks
// ---------------------------------------------------------------------------

const mockGet = vi.fn()
const mockPost = vi.fn()
const mockPatch = vi.fn()
const mockDel = vi.fn()

vi.mock('~/composables/useApi', () => ({
  useApi: () => ({
    get: mockGet,
    post: mockPost,
    patch: mockPatch,
    put: vi.fn(),
    delete: mockDel,
    postFormData: vi.fn(),
  }),
}))

vi.mock('~/composables/useToast', () => ({
  useToast: () => ({
    showSuccess: vi.fn(),
    showError: vi.fn(),
    showInfo: vi.fn(),
    showToast: vi.fn(),
    toasts: { value: [] },
    clearAll: vi.fn(),
  }),
}))

vi.mock('~/composables/useConstructeurs', () => ({
  useConstructeurs: () => ({
    ensureConstructeurs: vi.fn().mockResolvedValue([]),
  }),
}))

beforeEach(() => {
  vi.clearAllMocks()
  const { clearComposantsCache } = useComposants()
  clearComposantsCache()
})

// ---------------------------------------------------------------------------
// createComposant
// ---------------------------------------------------------------------------
describe('createComposant', () => {
  it('sends all fields in creation payload', async () => {
    const created = { ...mockComponentFromApi, id: 'comp-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createComposant } = useComposants()
    const result = await createComposant({
      name: 'Moteur principal',
      reference: 'COMP-MOT-001',
      description: 'Un moteur',
      typeComposantId: 'tc-moteur',
    })

    expect(result.success).toBe(true)
    // normalizeRelationIds converts typeComposantId to typeComposant IRI
    expect(mockPost).toHaveBeenCalledWith('/composants', expect.objectContaining({
      name: 'Moteur principal',
      reference: 'COMP-MOT-001',
      description: 'Un moteur',
      typeComposant: '/api/model_types/tc-moteur',
    }))
    // typeComposantId should be removed by normalizeRelationIds
    const payload = mockPost.mock.calls[0]![1]
    expect(payload).not.toHaveProperty('typeComposantId')
  })

  it('adds created composant to cache', async () => {
    const created = { ...mockComponentFromApi, id: 'comp-new', name: 'Nouveau composant' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createComposant, composants, total } = useComposants()

    expect(composants.value).toHaveLength(0)
    expect(total.value).toBe(0)

    await createComposant({ name: 'Nouveau composant' })

    expect(composants.value).toHaveLength(1)
    expect(composants.value[0]!.id).toBe('comp-new')
    expect(total.value).toBe(1)
  })
})

// ---------------------------------------------------------------------------
// updateComposant
// ---------------------------------------------------------------------------
describe('updateComposant', () => {
  it('patches and updates cache', async () => {
    // Seed the cache with one composant
    const original = { ...mockComponentFromApi, id: 'comp-001', name: 'Ancien nom' }
    mockPost.mockResolvedValue({ success: true, data: original })
    const { createComposant, updateComposant, composants } = useComposants()
    await createComposant({ name: 'Ancien nom' })
    expect(composants.value).toHaveLength(1)

    // Now update
    const updated = { ...original, name: 'Nouveau nom' }
    mockPatch.mockResolvedValue({ success: true, data: updated })

    const result = await updateComposant('comp-001', { name: 'Nouveau nom' })

    expect(result.success).toBe(true)
    expect(mockPatch).toHaveBeenCalledWith('/composants/comp-001', expect.objectContaining({
      name: 'Nouveau nom',
    }))
    expect(composants.value[0]!.name).toBe('Nouveau nom')
  })
})

// ---------------------------------------------------------------------------
// deleteComposant
// ---------------------------------------------------------------------------
describe('deleteComposant', () => {
  it('removes composant from cache on success', async () => {
    // Seed cache
    const item = { ...mockComponentFromApi, id: 'comp-del', name: 'A supprimer' }
    mockPost.mockResolvedValue({ success: true, data: item })
    const { createComposant, deleteComposant, composants, total } = useComposants()
    await createComposant({ name: 'A supprimer' })
    expect(composants.value).toHaveLength(1)
    expect(total.value).toBe(1)

    mockDel.mockResolvedValue({ success: true })
    const result = await deleteComposant('comp-del')

    expect(result.success).toBe(true)
    expect(composants.value).toHaveLength(0)
    expect(total.value).toBe(0)
  })

  it('keeps composant in cache on failure', async () => {
    // Seed cache
    const item = { ...mockComponentFromApi, id: 'comp-keep', name: 'Garder' }
    mockPost.mockResolvedValue({ success: true, data: item })
    const { createComposant, deleteComposant, composants, total } = useComposants()
    await createComposant({ name: 'Garder' })
    expect(composants.value).toHaveLength(1)

    mockDel.mockResolvedValue({ success: false, error: 'Server error' })
    const result = await deleteComposant('comp-keep')

    expect(result.success).toBe(false)
    expect(composants.value).toHaveLength(1)
    expect(composants.value[0]!.id).toBe('comp-keep')
  })
})
