import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import Scanner from '@/components/Scanner'
import { inventoryApi } from '@/lib/api'

vi.mock('@/lib/api')

// ============================================================================
// INTEGRATION TEST: Scanner Workflow
// ============================================================================

describe('Scanner Workflow Integration', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  // ============================================================================
  // E2E: Scan → Match → Adjust Stock
  // ============================================================================

  describe('End-to-End: Scan → Match → Adjust Stock', () => {
    it('should complete full scan workflow with QR code', async () => {
      // Setup
      const mockItems = [
        { id: 1, name: 'Widget A', barcode: '1234567890', quantity: 10 },
        { id: 2, name: 'Widget B', barcode: '0987654321', quantity: 5 },
      ]

      vi.mocked(inventoryApi.getItems).mockResolvedValue(mockItems)
      vi.mocked(inventoryApi.updateItemQuantity).mockResolvedValue({
        id: 1,
        quantity: 15,
      })

      const onScanSuccess = vi.fn()
      const onOCRMatch = vi.fn()

      // Render scanner component
      const { container } = render(
        <Scanner
          onScanSuccess={onScanSuccess}
          onOCRMatch={onOCRMatch}
          paused={false}
        />
      )

      // Verify scanner renders
      expect(container.querySelector('.aspect-square')).toBeInTheDocument()
    })

    it('should match scanned barcode to inventory item', async () => {
      const mockItems = [
        { id: 1, name: 'Component C', barcode: 'QR-12345', quantity: 0 },
      ]

      vi.mocked(inventoryApi.getItems).mockResolvedValue(mockItems)
      vi.mocked(inventoryApi.findItemByBarcode).mockResolvedValue(mockItems[0])

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should update stock quantity after scan', async () => {
      vi.mocked(inventoryApi.updateItemQuantity).mockResolvedValue({
        id: 1,
        name: 'Updated Item',
        quantity: 20,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle checkout operation after scan', async () => {
      const mockItem = { id: 1, name: 'Item', barcode: 'ABC123', quantity: 5 }

      vi.mocked(inventoryApi.updateItemQuantity).mockResolvedValue({
        ...mockItem,
        quantity: 4,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle checkin operation after scan', async () => {
      const mockItem = { id: 1, name: 'Item', barcode: 'ABC123', quantity: 10 }

      vi.mocked(inventoryApi.updateItemQuantity).mockResolvedValue({
        ...mockItem,
        quantity: 11,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle multiple consecutive scans', async () => {
      const mockItems = [
        { id: 1, name: 'Item 1', barcode: '111', quantity: 10 },
        { id: 2, name: 'Item 2', barcode: '222', quantity: 5 },
      ]

      vi.mocked(inventoryApi.getItems).mockResolvedValue(mockItems)
      vi.mocked(inventoryApi.updateItemQuantity).mockResolvedValue(mockItems[0])

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle scan of non-existent item', async () => {
      vi.mocked(inventoryApi.findItemByBarcode).mockResolvedValue(null)

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should pause and resume scanning', async () => {
      const onScanSuccess = vi.fn()
      const { container, rerender } = render(
        <Scanner onScanSuccess={onScanSuccess} paused={false} />
      )

      expect(container).toBeInTheDocument()

      rerender(
        <Scanner onScanSuccess={onScanSuccess} paused={true} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle API errors during stock update', async () => {
      vi.mocked(inventoryApi.updateItemQuantity).mockRejectedValue(
        new Error('Network error')
      )

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should display error message on failed scan', async () => {
      vi.mocked(inventoryApi.getItems).mockRejectedValue(
        new Error('API error')
      )

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })
  })

  // ============================================================================
  // E2E: Scan with OCR Matching
  // ============================================================================

  describe('End-to-End: Scan with OCR Matching', () => {
    it('should match OCR extracted data to inventory', async () => {
      const mockOCRResult = {
        name: 'Extracted Part Number',
        partNumber: 'PART-001',
        quantity: 10,
      }

      vi.mocked(inventoryApi.matchOCRResult).mockResolvedValue({
        id: 1,
        name: 'Widget A',
        barcode: 'extracted_match',
      })

      const onOCRMatch = vi.fn()
      const { container } = render(
        <Scanner onOCRMatch={onOCRMatch} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle OCR validation and confirmation', async () => {
      vi.mocked(inventoryApi.matchOCRResult).mockResolvedValue({
        id: 2,
        name: 'Component',
        confidence: 0.95,
      })

      const onOCRMatch = vi.fn()
      const { container } = render(
        <Scanner onOCRMatch={onOCRMatch} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should create new item from OCR if no match found', async () => {
      vi.mocked(inventoryApi.createItem).mockResolvedValue({
        id: 999,
        name: 'New OCR Item',
        barcode: 'NEW-BARCODE',
      })

      const onOCRMatch = vi.fn()
      const { container } = render(
        <Scanner onOCRMatch={onOCRMatch} />
      )

      expect(container).toBeInTheDocument()
    })
  })

  // ============================================================================
  // E2E: Error Recovery
  // ============================================================================

  describe('Error Recovery', () => {
    it('should recover from network error and retry', async () => {
      let callCount = 0
      vi.mocked(inventoryApi.updateItemQuantity).mockImplementation(() => {
        callCount++
        if (callCount === 1) {
          return Promise.reject(new Error('Network error'))
        }
        return Promise.resolve({ id: 1, quantity: 15 })
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should handle timeout during scan operation', async () => {
      vi.mocked(inventoryApi.getItems).mockImplementation(
        () => new Promise((_, reject) =>
          setTimeout(() => reject(new Error('Timeout')), 5000)
        )
      )

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should maintain state consistency after error', async () => {
      vi.mocked(inventoryApi.updateItemQuantity).mockRejectedValueOnce(
        new Error('Conflict')
      ).mockResolvedValueOnce({
        id: 1,
        quantity: 10,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })
  })

  // ============================================================================
  // E2E: Offline Sync Integration
  // ============================================================================

  describe('Offline Sync Integration', () => {
    it('should queue scan operation when offline', async () => {
      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should sync queued operations when online', async () => {
      vi.mocked(inventoryApi.bulkSync).mockResolvedValue({
        synced: 5,
        failed: 0,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })

    it('should preserve UUID idempotency during sync', async () => {
      vi.mocked(inventoryApi.bulkSync).mockResolvedValue({
        synced: 3,
        skipped: 2,
      })

      const onScanSuccess = vi.fn()
      const { container } = render(
        <Scanner onScanSuccess={onScanSuccess} />
      )

      expect(container).toBeInTheDocument()
    })
  })

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