import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Hono } from 'hono'
import type { AppEnv } from '../src/types'

const mockIssueTaxInvoice = vi.fn()
const mockIssueTaxInvoiceTx = vi.fn()
const mockVoidInvoiceTx = vi.fn()
const mockGetInvoice = vi.fn()
const mockGenerateAndStoreInvoiceSnapshot = vi.fn()
const mockGetTenantReminderSettings = vi.fn()
const mockRecordInvoicePayment = vi.fn()
const mockGetInvoiceAdapterSettings = vi.fn()
const mockPostIssue = vi.fn()
const mockReverseMovement = vi.fn()

const queueSend = vi.fn()

vi.mock('../src/middleware/auth', () => ({
  authMiddleware: async (c: { set: (key: string, value: unknown) => void }, next: () => Promise<void>) => {
    c.set('session', {
      type: 'user',
      sub: 'user-1',
      tid: 'tenant-1',
      role: 'OWNER',
      permissions: ['invoices:write'],
    })
    c.set('db', {
      transaction: async (callback: (tx: unknown) => Promise<unknown>) => callback({ tx: true }),
    })
    await next()
  },
}))

vi.mock('../src/middleware/guards', () => ({
  requirePermission: () => async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('../src/middleware/require-module-enabled', () => ({
  requireModuleEnabled: () => async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('../src/middleware/bump-financials-version', () => ({
  bumpFinancialsVersionOnWrite: async (_c: unknown, next: () => Promise<void>) => next(),
}))

vi.mock('@zync/db/queries', async () => {
  const actual = await vi.importActual<Record<string, unknown>>('@zync/db/queries')
  return {
    ...actual,
    issueTaxInvoice: (...args: unknown[]) => mockIssueTaxInvoice(...args),
    issueTaxInvoiceTx: (...args: unknown[]) => mockIssueTaxInvoiceTx(...args),
    voidInvoiceTx: (...args: unknown[]) => mockVoidInvoiceTx(...args),
    getInvoice: (...args: unknown[]) => mockGetInvoice(...args),
    getTenantReminderSettings: (...args: unknown[]) => mockGetTenantReminderSettings(...args),
    recordInvoicePayment: (...args: unknown[]) => mockRecordInvoicePayment(...args),
    getInvoiceAdapterSettings: (...args: unknown[]) => mockGetInvoiceAdapterSettings(...args),
  }
})

vi.mock('../src/lib/invoice-snapshot', () => ({
  generateAndStoreInvoiceSnapshot: (...args: unknown[]) => mockGenerateAndStoreInvoiceSnapshot(...args),
}))

vi.mock('../src/integrations/platform/inventory', () => ({
  postIssue: (...args: unknown[]) => mockPostIssue(...args),
  reverseMovement: (...args: unknown[]) => mockReverseMovement(...args),
}))

import { invoiceRoutes } from '../src/routes/invoices/index'

function appForInvoices() {
  const app = new Hono<AppEnv>()
  app.route('/api/invoices', invoiceRoutes)
  return app
}

describe('invoice adapter queue enqueue points', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockIssueTaxInvoice.mockResolvedValue({
      id: 'inv-1',
      status: 'TAX_ISSUED',
      invoiceNumber: 'INV-1',
      dueDate: null,
    })
    mockIssueTaxInvoiceTx.mockResolvedValue({
      id: 'inv-1',
      status: 'TAX_ISSUED',
      invoiceNumber: 'INV-1',
      dueDate: null,
    })
    mockVoidInvoiceTx.mockResolvedValue({
      id: 'inv-1',
      status: 'VOID',
      invoiceNumber: 'INV-1',
      dueDate: null,
    })
    mockGetInvoice.mockResolvedValue({
      id: 'inv-1',
      status: 'PAID',
      source: 'manual',
    })
    mockGenerateAndStoreInvoiceSnapshot.mockResolvedValue(null)
    mockGetTenantReminderSettings.mockResolvedValue({ enabled: false, schedule: [] })
    mockRecordInvoicePayment.mockResolvedValue({ payments: [], amountPaid: 100, balance: 0, total: 100, overpaymentAmount: 0 })
    mockGetInvoiceAdapterSettings.mockResolvedValue({
      adapter: 'morning',
      autoSyncOnTaxIssue: true,
      autoDraftFromRetainer: false,
      autoSendRetainerInvoice: false,
      taskStatusTrigger: null,
    })
    mockPostIssue.mockResolvedValue(undefined)
    mockReverseMovement.mockResolvedValue(undefined)
  })

  it('enqueues invoice.push when a tax invoice is issued and auto-sync is enabled', async () => {
    const res = await appForInvoices().request(
      '/api/invoices/inv-1/issue-tax',
      { method: 'POST' },
      { QUEUE: { send: queueSend } } as AppEnv['Bindings'],
    )

    expect(res.status).toBe(200)
    expect(mockIssueTaxInvoiceTx).toHaveBeenCalledWith(
      { tx: true },
      'tenant-1',
      'inv-1',
      'user-1',
      expect.any(String),
      'IL',
    )
    expect(mockPostIssue).toHaveBeenCalledWith(
      { tx: true },
      { tenantId: 'tenant-1', invoiceId: 'inv-1' },
    )
    expect(queueSend).toHaveBeenCalledWith({ type: 'invoice.push', invoiceId: 'inv-1', tenantId: 'tenant-1' })
  })

  it('reverses inventory inline before voiding the invoice', async () => {
    const res = await appForInvoices().request(
      '/api/invoices/inv-1/void',
      {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ reason: 'customer cancellation' }),
      },
    )

    expect(res.status).toBe(200)
    expect(mockReverseMovement).toHaveBeenCalledWith(
      { tx: true },
      { tenantId: 'tenant-1', invoiceId: 'inv-1' },
    )
    expect(mockVoidInvoiceTx).toHaveBeenCalledWith(
      { tx: true },
      'tenant-1',
      'inv-1',
      'user-1',
      'customer cancellation',
    )
  })

  it('enqueues invoice.payment_sync when a payment moves the invoice to PAID', async () => {
    const res = await appForInvoices().request(
      '/api/invoices/inv-1/record-payment',
      {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          amount: 100,
          paymentDate: '2026-07-01',
          paymentMethod: 'manual',
        }),
      },
      { QUEUE: { send: queueSend } } as AppEnv['Bindings'],
    )

    expect(res.status).toBe(200)
    expect(queueSend).toHaveBeenCalledWith({ type: 'invoice.payment_sync', invoiceId: 'inv-1', tenantId: 'tenant-1' })
  })
})
