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

import { usePieces } from '~/composables/usePieces'
import { mockPieceFromApi, 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 { clearPiecesCache } = usePieces()
  clearPiecesCache()
})

// ---------------------------------------------------------------------------
// createPiece
// ---------------------------------------------------------------------------
describe('createPiece', () => {
  it('sends all fields including prix in POST payload', async () => {
    const created = { ...mockPieceFromApi, id: 'piece-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createPiece } = usePieces()
    await createPiece({
      name: 'Roulement 6205',
      reference: 'ROUL-6205',
      prix: 25.50,
      typePieceId: 'tp-bearing-001',
    } as any)

    expect(mockPost).toHaveBeenCalledWith('/pieces', expect.objectContaining({
      name: 'Roulement 6205',
      reference: 'ROUL-6205',
      prix: 25.50,
      typePiece: '/api/model_types/tp-bearing-001',
    }))
  })

  it('strips constructeur fields from payload', async () => {
    const created = { ...mockPieceFromApi, id: 'piece-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createPiece } = usePieces()
    await createPiece({
      name: 'Test Piece',
      constructeurIds: ['cstr-skf-001'],
      constructeurs: [{ id: 'cstr-skf-001', name: 'SKF' }] as any,
    })

    const payload = mockPost.mock.calls[0]![1]
    expect(payload).not.toHaveProperty('constructeurIds')
    expect(payload).not.toHaveProperty('constructeurs')
    expect(payload).not.toHaveProperty('constructeurId')
    expect(payload).not.toHaveProperty('constructeur')
  })

  it('adds created piece to cache (pieces array and total)', async () => {
    const created = { ...mockPieceFromApi, id: 'piece-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createPiece, pieces, total } = usePieces()
    const result = await createPiece({ name: 'New Piece' })

    expect(result.success).toBe(true)
    expect(pieces.value).toHaveLength(1)
    expect(pieces.value[0]!.id).toBe('piece-new')
    expect(total.value).toBe(1)
  })
})

// ---------------------------------------------------------------------------
// updatePiece
// ---------------------------------------------------------------------------
describe('updatePiece', () => {
  it('patches with supplied fields and updates cache', async () => {
    // Seed cache first
    const original = { ...mockPieceFromApi }
    mockPost.mockResolvedValue({ success: true, data: original })
    const { createPiece, updatePiece, pieces } = usePieces()
    await createPiece({ name: 'Roulement 6205' })

    const updated = { ...mockPieceFromApi, name: 'Updated Name', reference: 'ROUL-NEW' }
    mockPatch.mockResolvedValue({ success: true, data: updated })

    const result = await updatePiece(mockPieceFromApi.id, {
      name: 'Updated Name',
      reference: 'ROUL-NEW',
    })

    expect(mockPatch).toHaveBeenCalledWith(`/pieces/${mockPieceFromApi.id}`, expect.objectContaining({
      name: 'Updated Name',
      reference: 'ROUL-NEW',
    }))
    expect(result.success).toBe(true)
    expect(pieces.value.find(p => p.id === mockPieceFromApi.id)?.name).toBe('Updated Name')
  })
})

// ---------------------------------------------------------------------------
// deletePiece
// ---------------------------------------------------------------------------
describe('deletePiece', () => {
  it('removes piece from cache on success', async () => {
    // Seed cache
    mockPost.mockResolvedValue({ success: true, data: { ...mockPieceFromApi } })
    const { createPiece, deletePiece, pieces, total } = usePieces()
    await createPiece({ name: 'To Delete' })
    expect(pieces.value).toHaveLength(1)

    mockDel.mockResolvedValue({ success: true })
    const result = await deletePiece(mockPieceFromApi.id)

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

  it('does not remove on failure', async () => {
    // Seed cache
    mockPost.mockResolvedValue({ success: true, data: { ...mockPieceFromApi } })
    const { createPiece, deletePiece, pieces, total } = usePieces()
    await createPiece({ name: 'Should Stay' })
    expect(pieces.value).toHaveLength(1)

    mockDel.mockResolvedValue({ success: false, error: 'Server error' })
    const result = await deletePiece(mockPieceFromApi.id)

    expect(result.success).toBe(false)
    expect(pieces.value).toHaveLength(1)
    expect(total.value).toBe(1)
  })
})
