import { it, expect, vi } from 'vitest'
import {
  createJobRegistry,
  TerminalJobError,
  type JobEnvelope,
  type QueueBatch,
  type QueueMessage,
} from './index.js'
import { consume, enqueue, resolveQueue } from './cf-queues.js'

type Env = Record<string, never>

function okSchema() {
  return {
    '~standard': {
      version: 1 as const,
      vendor: 'test',
      validate: (value: unknown) => ({ value: value as { n: number } }),
    },
  }
}

function fakeMessage(body: JobEnvelope): QueueMessage<JobEnvelope> {
  return {
    id: '1',
    timestamp: new Date(),
    body,
    attempts: 1,
    ack: vi.fn(),
    retry: vi.fn(),
  }
}

function fakeBatch(...messages: QueueMessage<JobEnvelope>[]): QueueBatch<JobEnvelope> {
  return {
    queue: 'test-queue',
    messages,
    retryAll: vi.fn(),
    ackAll: vi.fn(),
  }
}

it('enqueue sends body via queue.send', async () => {
  const send = vi.fn(async () => {})
  await enqueue({ send }, { type: 'x', payload: { n: 1 } })
  expect(send).toHaveBeenCalledWith({ type: 'x', payload: { n: 1 } })
})

it('resolveQueue returns binding by name', () => {
  const q = { send: vi.fn() }
  expect(resolveQueue({ mail: q }, 'mail')).toBe(q)
  expect(() => resolveQueue({}, 'missing')).toThrow('queue binding not found')
})

it('consume acks on successful dispatch', async () => {
  const registry = createJobRegistry<Env>()
  registry.register('ok', okSchema(), async () => {})
  const msg = fakeMessage({ type: 'ok', payload: { n: 1 } })
  await consume(registry, fakeBatch(msg), {})
  expect(msg.ack).toHaveBeenCalledOnce()
  expect(msg.retry).not.toHaveBeenCalled()
})

it('consume retries on transient throw', async () => {
  const registry = createJobRegistry<Env>()
  registry.register('boom', okSchema(), async () => {
    throw new Error('transient')
  })
  const msg = fakeMessage({ type: 'boom', payload: { n: 1 } })
  await consume(registry, fakeBatch(msg), {})
  expect(msg.retry).toHaveBeenCalledOnce()
  expect(msg.ack).not.toHaveBeenCalled()
})

it('consume acks on TerminalJobError and malformed payload', async () => {
  const registry = createJobRegistry<Env>()
  const onFail = vi.fn()
  registry.register('term', okSchema(), async () => {
    throw new TerminalJobError('terminal')
  })
  registry.register('bad', {
    '~standard': {
      version: 1 as const,
      vendor: 'test',
      validate: () => ({ issues: [{ message: 'bad' }] }),
    },
  }, async () => {})

  const termMsg = fakeMessage({ type: 'term', payload: { n: 1 } })
  await consume(registry, fakeBatch(termMsg), {}, { onTerminalFailure: onFail })
  expect(termMsg.ack).toHaveBeenCalledOnce()
  expect(onFail).toHaveBeenCalled()

  const badMsg = fakeMessage({ type: 'bad', payload: {} })
  await consume(registry, fakeBatch(badMsg), {})
  expect(badMsg.ack).toHaveBeenCalledOnce()
  expect(badMsg.retry).not.toHaveBeenCalled()
})
