import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { MessageBatch } from '@cloudflare/workers-types'

const mockGetExpenseById = vi.fn()
const mockSetExpenseStatus = vi.fn()
const mockGetExpenseSettings = vi.fn()
const mockRunOcr = vi.fn()
const mockEvaluateDeductibility = vi.fn()
const mockEmitExpenseWebhook = vi.fn()
const mockSerializeExpense = vi.fn()
const mockPushOverWebSocket = vi.fn().mockResolvedValue(undefined)
const mockResolveApprovalStatus = vi.fn()
const mockCreateDb = vi.fn()
const mockPostExpenseReceipts = vi.fn().mockResolvedValue(1)

vi.mock('@zync/db/queries', () => ({
  createDb: (...args: unknown[]) => mockCreateDb(...args),
  resolveApprovalStatus: (...args: unknown[]) => mockResolveApprovalStatus(...args),
}))

vi.mock('@zync/expenses', () => ({
  getExpenseById: (...args: unknown[]) => mockGetExpenseById(...args),
  setExpenseStatus: (...args: unknown[]) => mockSetExpenseStatus(...args),
  getExpenseSettings: (...args: unknown[]) => mockGetExpenseSettings(...args),
  runOcr: (...args: unknown[]) => mockRunOcr(...args),
  evaluateDeductibility: (...args: unknown[]) => mockEvaluateDeductibility(...args),
  emitExpenseWebhook: (...args: unknown[]) => mockEmitExpenseWebhook(...args),
  serializeExpense: (...args: unknown[]) => mockSerializeExpense(...args),
  ExpenseProcessingError: class ExpenseProcessingError extends Error {
    kind: 'terminal' | 'transient'
    constructor(message: string, kind: 'terminal' | 'transient' = 'terminal') {
      super(message)
      this.kind = kind
    }
  },
}))

vi.mock('@zync/notifications', () => ({
  pushOverWebSocket: (...args: unknown[]) => mockPushOverWebSocket(...args),
}))

vi.mock('../src/integrations/platform/inventory', () => ({
  postExpenseReceipts: (...args: unknown[]) => mockPostExpenseReceipts(...args),
}))

import { handleExpenseProcess } from '../src/queues/expense-process'

describe('expense process inventory receipt integration', () => {
  beforeEach(() => {
    vi.clearAllMocks()

    const db = {}
    mockCreateDb.mockReturnValue(db)

    mockGetExpenseById
      .mockResolvedValueOnce({
        id: 'expense-1',
        tenantId: 'tenant-1',
        createdBy: 'user-1',
        r2Key: 'tenant-1/expenses/expense-1/receipt.pdf',
        fileType: 'pdf',
      })
      .mockResolvedValueOnce({
        id: 'expense-1',
        tenantId: 'tenant-1',
        createdBy: 'user-1',
        amount: '118.00',
        vatAmount: '18.00',
        updatedAt: new Date('2026-07-05T10:00:00.000Z'),
        createdAt: new Date('2026-07-05T09:00:00.000Z'),
        sourceMetadata: {
          stockLines: [{ stockItemId: 'item-1', locationId: 'loc-1', qty: 2, netAmount: 100 }],
        },
      })

    mockSerializeExpense.mockImplementation((row: Record<string, unknown>) => ({
      id: row.id,
      tenantId: row.tenantId,
      createdBy: row.createdBy,
      r2Key: row.r2Key,
      fileType: row.fileType,
      amount: row.amount ?? null,
      vatAmount: row.vatAmount ?? null,
      updatedAt:
        row.updatedAt instanceof Date
          ? row.updatedAt.toISOString()
          : '2026-07-05T10:00:00.000Z',
      createdAt:
        row.createdAt instanceof Date
          ? row.createdAt.toISOString()
          : '2026-07-05T09:00:00.000Z',
      sourceMetadata: row.sourceMetadata ?? null,
    }))

    mockGetExpenseSettings.mockResolvedValue({ business_category: 'retail' })
    mockRunOcr.mockResolvedValue({
      vendorName: 'Vendor',
      vendorTaxId: null,
      invoiceNumber: 'INV-1',
      invoiceTotal: 118,
      vatAmount: '18.00',
      vatDeductible: true,
      currency: 'ILS',
      allocationNumber: null,
      rawOcrText: 'ocr',
      ocrConfidence: 0.92,
      expenseDate: '2026-07-05',
      amount: '118.00',
      fxRateMissing: false,
    })
    mockEvaluateDeductibility.mockResolvedValue({
      expenseCategory: 'equipment',
      deductionPct: 100,
      deductionConfidence: 0.95,
      reasoningHe: 'ok',
      reasoningEn: 'ok',
    })
    mockResolveApprovalStatus.mockResolvedValue('not_required')
  })

  it('posts receipts for stock lines when OCR auto-completes an expense', async () => {
    const env = {
      STORAGE: {
        get: vi.fn().mockResolvedValue({
          arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(8)),
          httpMetadata: { contentType: 'application/pdf' },
        }),
      },
    }

    const ack = vi.fn()
    const retry = vi.fn()

    await handleExpenseProcess({
      queue: 'expense-process',
      messages: [{
        body: {
          type: 'expense.process',
          tenantId: 'tenant-1',
          expenseId: 'expense-1',
          tier: 'business',
        },
        ack,
        retry,
      }],
    } as unknown as MessageBatch<{
      type: 'expense.process'
      tenantId: string
      expenseId: string
      tier: string
    }>, env as never)

    expect(mockPostExpenseReceipts).toHaveBeenCalledWith(
      expect.anything(),
      expect.objectContaining({
        id: 'expense-1',
        tenantId: 'tenant-1',
        amount: '118.00',
        vatAmount: '18.00',
      }),
    )
    expect(ack).toHaveBeenCalledOnce()
    expect(retry).not.toHaveBeenCalled()
  })
})
