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

import { useProducts } from '~/composables/useProducts'
import { mockProductFromApi, mockConstructeurSKF, 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 { clearProductsCache } = useProducts()
  clearProductsCache()
})

// ---------------------------------------------------------------------------
// createProduct
// ---------------------------------------------------------------------------
describe('createProduct', () => {
  it('sends all fields including supplierPrice in POST payload', async () => {
    const created = { ...mockProductFromApi, id: 'prod-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createProduct } = useProducts()
    await createProduct({
      name: 'Graisse LGMT2',
      reference: 'LUB-LGMT2',
      supplierPrice: 45.90,
      typeProductId: 'tprod-grease-001',
    })

    expect(mockPost).toHaveBeenCalledWith('/products', expect.objectContaining({
      name: 'Graisse LGMT2',
      reference: 'LUB-LGMT2',
      supplierPrice: 45.90,
      typeProduct: '/api/model_types/tprod-grease-001',
    }))
  })

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

    const { createProduct } = useProducts()
    await createProduct({
      name: 'Test Product',
      constructeurIds: ['cstr-skf-001'],
      constructeurs: [mockConstructeurSKF] 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 product to cache (products array and total)', async () => {
    const created = { ...mockProductFromApi, id: 'prod-new' }
    mockPost.mockResolvedValue({ success: true, data: created })

    const { createProduct, products, total } = useProducts()
    const result = await createProduct({ name: 'New Product' })

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

// ---------------------------------------------------------------------------
// updateProduct
// ---------------------------------------------------------------------------
describe('updateProduct', () => {
  it('patches with supplied fields and updates cache', async () => {
    // Seed cache first
    const original = { ...mockProductFromApi }
    mockPost.mockResolvedValue({ success: true, data: original })
    const { createProduct, updateProduct, products } = useProducts()
    await createProduct({ name: 'Graisse LGMT2' })

    const updated = { ...mockProductFromApi, name: 'Updated Name', supplierPrice: 99.99 }
    mockPatch.mockResolvedValue({ success: true, data: updated })

    const result = await updateProduct(mockProductFromApi.id, {
      name: 'Updated Name',
      supplierPrice: 99.99,
    })

    expect(mockPatch).toHaveBeenCalledWith(`/products/${mockProductFromApi.id}`, expect.objectContaining({
      name: 'Updated Name',
      supplierPrice: 99.99,
    }))
    expect(result.success).toBe(true)
    expect(products.value.find(p => p.id === mockProductFromApi.id)?.name).toBe('Updated Name')
  })
})

// ---------------------------------------------------------------------------
// deleteProduct
// ---------------------------------------------------------------------------
describe('deleteProduct', () => {
  it('removes product from cache on success', async () => {
    // Seed cache
    mockPost.mockResolvedValue({ success: true, data: { ...mockProductFromApi } })
    const { createProduct, deleteProduct, products, total } = useProducts()
    await createProduct({ name: 'To Delete' })
    expect(products.value).toHaveLength(1)

    mockDel.mockResolvedValue({ success: true })
    const result = await deleteProduct(mockProductFromApi.id)

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

  it('does not remove on failure', async () => {
    // Seed cache
    mockPost.mockResolvedValue({ success: true, data: { ...mockProductFromApi } })
    const { createProduct, deleteProduct, products, total } = useProducts()
    await createProduct({ name: 'Should Stay' })
    expect(products.value).toHaveLength(1)

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

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

// ---------------------------------------------------------------------------
// getProduct
// ---------------------------------------------------------------------------
describe('getProduct', () => {
  it('returns cached product if available with constructeurs (no extra API call)', async () => {
    // Seed cache with a product that has resolved constructeurs
    const productWithConstructeurs = {
      ...mockProductFromApi,
      constructeurs: [mockConstructeurSKF],
    }
    mockPost.mockResolvedValue({ success: true, data: productWithConstructeurs })
    const { createProduct, getProduct } = useProducts()
    await createProduct({ name: 'Cached' })

    mockGet.mockClear()
    const result = await getProduct(mockProductFromApi.id)

    expect(result.success).toBe(true)
    expect(result.data?.id).toBe(mockProductFromApi.id)
    expect(mockGet).not.toHaveBeenCalled()
  })

  it('fetches from API with force: true', async () => {
    // Seed cache with a product that has resolved constructeurs
    const productWithConstructeurs = {
      ...mockProductFromApi,
      constructeurs: [mockConstructeurSKF],
    }
    mockPost.mockResolvedValue({ success: true, data: productWithConstructeurs })
    const { createProduct, getProduct } = useProducts()
    await createProduct({ name: 'Cached' })

    const freshData = { ...mockProductFromApi, name: 'Fresh from API' }
    mockGet.mockResolvedValue({ success: true, data: freshData })

    const result = await getProduct(mockProductFromApi.id, { force: true })

    expect(mockGet).toHaveBeenCalledWith(`/products/${mockProductFromApi.id}`)
    expect(result.success).toBe(true)
    expect(result.data?.name).toBe('Fresh from API')
  })
})
