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

import { wrapCollection } from '../fixtures/mockData'

// ---------------------------------------------------------------------------
// Import under test (AFTER all vi.mock calls)
// ---------------------------------------------------------------------------

import { useDocuments } from '~/composables/useDocuments'

// ---------------------------------------------------------------------------
// Mocks — API layer
// ---------------------------------------------------------------------------

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

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

// ---------------------------------------------------------------------------
// Mocks — Toast
// ---------------------------------------------------------------------------

const mockShowSuccess = vi.fn()
const mockShowError = vi.fn()

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

// ---------------------------------------------------------------------------
// Test data
// ---------------------------------------------------------------------------

const mockDocument = {
  id: 'doc-001',
  name: 'photo.jpg',
  filename: 'photo.jpg',
  mimeType: 'image/jpeg',
  size: 12345,
  fileUrl: '/files/photo.jpg',
  downloadUrl: '/files/photo.jpg/download',
  createdAt: '2025-01-10T08:00:00+00:00',
}

const mockDocument2 = {
  id: 'doc-002',
  name: 'schema.pdf',
  filename: 'schema.pdf',
  mimeType: 'application/pdf',
  size: 54321,
  fileUrl: '/files/schema.pdf',
  downloadUrl: '/files/schema.pdf/download',
  createdAt: '2025-01-11T09:00:00+00:00',
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function createMockFile(name: string, type = 'image/jpeg'): File {
  return new File(['content'], name, { type })
}

// ---------------------------------------------------------------------------
// beforeEach
// ---------------------------------------------------------------------------

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

// ---------------------------------------------------------------------------
// uploadDocuments — FormData is built correctly
// ---------------------------------------------------------------------------

describe('uploadDocuments', () => {
  it('builds FormData with file and context fields', async () => {
    mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })

    const { uploadDocuments } = useDocuments()
    const file = createMockFile('photo.jpg')

    await uploadDocuments({
      files: [file],
      context: { pieceId: 'piece-001', composantId: 'comp-001' },
    })

    expect(mockPostFormData).toHaveBeenCalledTimes(1)
    const [endpoint, formData] = mockPostFormData.mock.calls[0]!
    expect(endpoint).toBe('/documents')

    expect(formData).toBeInstanceOf(FormData)
    expect(formData.get('file')).toBe(file)
    expect(formData.get('name')).toBe('photo.jpg')
    expect(formData.get('pieceId')).toBe('piece-001')
    expect(formData.get('composantId')).toBe('comp-001')
  })

  it('uploads multiple files separately', async () => {
    mockPostFormData
      .mockResolvedValueOnce({ success: true, data: mockDocument })
      .mockResolvedValueOnce({ success: true, data: mockDocument2 })

    const { uploadDocuments } = useDocuments()
    const file1 = createMockFile('photo.jpg')
    const file2 = createMockFile('schema.pdf', 'application/pdf')

    const result = await uploadDocuments({
      files: [file1, file2],
      context: { machineId: 'machine-001' },
    })

    expect(mockPostFormData).toHaveBeenCalledTimes(2)

    // First call
    const [, formData1] = mockPostFormData.mock.calls[0]!
    expect(formData1.get('name')).toBe('photo.jpg')
    expect(formData1.get('machineId')).toBe('machine-001')

    // Second call
    const [, formData2] = mockPostFormData.mock.calls[1]!
    expect(formData2.get('name')).toBe('schema.pdf')
    expect(formData2.get('machineId')).toBe('machine-001')

    expect(result.success).toBe(true)
    expect(Array.isArray(result.data) ? result.data : []).toHaveLength(2)
  })

  it('appends type to FormData when provided in context', async () => {
    mockPostFormData.mockResolvedValue({ success: true, data: mockDocument })

    const { uploadDocuments } = useDocuments()
    const file = createMockFile('facture.pdf', 'application/pdf')

    await uploadDocuments({
      files: [file],
      context: { siteId: 'site-001', type: 'facture' },
    })

    const [, formData] = mockPostFormData.mock.calls[0]!
    expect(formData.get('type')).toBe('facture')
    expect(formData.get('siteId')).toBe('site-001')
  })

  it('returns error when no files provided', async () => {
    const { uploadDocuments } = useDocuments()

    const result = await uploadDocuments({ files: [], context: {} })

    expect(result.success).toBe(false)
    expect(mockPostFormData).not.toHaveBeenCalled()
  })
})

// ---------------------------------------------------------------------------
// loadDocumentsByComponent
// ---------------------------------------------------------------------------

describe('loadDocumentsByComponent', () => {
  it('calls correct endpoint /documents/composant/{id}', async () => {
    mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })

    const { loadDocumentsByComponent } = useDocuments()
    const result = await loadDocumentsByComponent('comp-001')

    expect(mockGet).toHaveBeenCalledTimes(1)
    expect(mockGet).toHaveBeenCalledWith('/documents/composant/comp-001')
    expect(result.success).toBe(true)
  })

  it('returns error for empty componentId', async () => {
    const { loadDocumentsByComponent } = useDocuments()
    const result = await loadDocumentsByComponent('')

    expect(mockGet).not.toHaveBeenCalled()
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// loadDocumentsByPiece
// ---------------------------------------------------------------------------

describe('loadDocumentsByPiece', () => {
  it('calls correct endpoint /documents/piece/{id}', async () => {
    mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })

    const { loadDocumentsByPiece } = useDocuments()
    const result = await loadDocumentsByPiece('piece-001')

    expect(mockGet).toHaveBeenCalledTimes(1)
    expect(mockGet).toHaveBeenCalledWith('/documents/piece/piece-001')
    expect(result.success).toBe(true)
  })

  it('returns error for empty pieceId', async () => {
    const { loadDocumentsByPiece } = useDocuments()
    const result = await loadDocumentsByPiece('')

    expect(mockGet).not.toHaveBeenCalled()
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// loadDocumentsByMachine
// ---------------------------------------------------------------------------

describe('loadDocumentsByMachine', () => {
  it('calls correct endpoint /documents/machine/{id}', async () => {
    mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })

    const { loadDocumentsByMachine } = useDocuments()
    const result = await loadDocumentsByMachine('machine-001')

    expect(mockGet).toHaveBeenCalledTimes(1)
    expect(mockGet).toHaveBeenCalledWith('/documents/machine/machine-001')
    expect(result.success).toBe(true)
  })

  it('returns error for empty machineId', async () => {
    const { loadDocumentsByMachine } = useDocuments()
    const result = await loadDocumentsByMachine('')

    expect(mockGet).not.toHaveBeenCalled()
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// loadDocumentsByProduct
// ---------------------------------------------------------------------------

describe('loadDocumentsByProduct', () => {
  it('calls correct endpoint /documents/product/{id}', async () => {
    mockGet.mockResolvedValue({ success: true, data: wrapCollection([mockDocument]) })

    const { loadDocumentsByProduct } = useDocuments()
    const result = await loadDocumentsByProduct('prod-001')

    expect(mockGet).toHaveBeenCalledTimes(1)
    expect(mockGet).toHaveBeenCalledWith('/documents/product/prod-001')
    expect(result.success).toBe(true)
  })

  it('returns error for empty productId', async () => {
    const { loadDocumentsByProduct } = useDocuments()
    const result = await loadDocumentsByProduct('')

    expect(mockGet).not.toHaveBeenCalled()
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// deleteDocument
// ---------------------------------------------------------------------------

describe('deleteDocument', () => {
  it('calls DELETE on correct endpoint', async () => {
    mockDel.mockResolvedValue({ success: true })

    const { deleteDocument } = useDocuments()
    const result = await deleteDocument('doc-001')

    expect(mockDel).toHaveBeenCalledTimes(1)
    expect(mockDel).toHaveBeenCalledWith('/documents/doc-001')
    expect(result.success).toBe(true)
  })

  it('removes from store when updateStore is true', async () => {
    mockGet.mockResolvedValue({
      success: true,
      data: wrapCollection([mockDocument, mockDocument2]),
    })
    mockDel.mockResolvedValue({ success: true })

    const { loadDocuments, deleteDocument, documents } = useDocuments()

    // Load documents into store first
    await loadDocuments({ force: true })

    expect(documents.value).toHaveLength(2)

    // Delete with updateStore: true
    await deleteDocument('doc-001', { updateStore: true })

    expect(documents.value).toHaveLength(1)
    expect(documents.value[0]!.id).toBe('doc-002')
  })

  it('shows success toast on successful delete', async () => {
    mockDel.mockResolvedValue({ success: true })

    const { deleteDocument } = useDocuments()
    await deleteDocument('doc-001')

    expect(mockShowSuccess).toHaveBeenCalledWith('Document supprimé')
  })
})
