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

const mockCreateDb = vi.fn(() => ({}))
const mockGetTenantById = vi.fn()
const mockRunAssistantTurn = vi.fn()
const mockLoadAdapterCredential = vi.fn()

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

vi.mock('@zync/ai/chat', () => ({
  runAssistantTurn: (...args: unknown[]) => mockRunAssistantTurn(...args),
}))

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

import { dispatchToAiAssistant } from '../src/ai/dispatch-to-assistant'
import { handleAiTelegramMessage } from '../src/queues/ai-telegram-message'

describe('dispatchToAiAssistant', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockGetTenantById.mockResolvedValue({ tier: 'business' })
  })

  it('enqueues a Telegram AI job only for the configured Telegram chat', async () => {
    const send = vi.fn().mockResolvedValue(undefined)
    await dispatchToAiAssistant(
      { QUEUE: { send } } as never,
      { chatId: 'chat-123', text: 'hello', metadata: { channel: 'telegram' } } as never,
      { autoCreateTickets: false, aiAssistantEnabled: true, telegramAiChatId: 'chat-123' },
      'tenant-1',
    )

    expect(send).toHaveBeenCalledWith({
      tenantId: 'tenant-1',
      telegramChatId: 'chat-123',
      message: 'hello',
    })
  })

  it('does not enqueue when the inbound Telegram chat does not match the configured AI chat', async () => {
    const send = vi.fn().mockResolvedValue(undefined)
    await dispatchToAiAssistant(
      { QUEUE: { send } } as never,
      { chatId: 'chat-999', text: 'hello', metadata: { channel: 'telegram' } } as never,
      { autoCreateTickets: false, aiAssistantEnabled: true, telegramAiChatId: 'chat-123' },
      'tenant-1',
    )

    expect(send).not.toHaveBeenCalled()
  })
})

describe('handleAiTelegramMessage', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockGetTenantById.mockResolvedValue({ tier: 'business' })
    mockRunAssistantTurn.mockResolvedValue({ reply: 'reply text', tokensUsed: 42 })
    mockLoadAdapterCredential.mockResolvedValue('telegram-token')
    vi.stubGlobal(
      'fetch',
      vi.fn().mockResolvedValue({
        ok: true,
      }),
    )
  })

  it('replies through Telegram for valid jobs', async () => {
    const ack = vi.fn()
    const retry = vi.fn()
    await handleAiTelegramMessage(
      {
        messages: [
          { body: { tenantId: 'tenant-1', telegramChatId: 'chat-123', message: 'hello' }, ack, retry },
        ],
      } as never,
      { INTEGRATION_ENCRYPTION_KEY: 'enc-key' } as never,
    )

    expect(mockRunAssistantTurn).toHaveBeenCalledWith(
      expect.anything(),
      expect.objectContaining({
        tenantId: 'tenant-1',
        channel: 'telegram',
        telegramChatId: 'chat-123',
        message: 'hello',
      }),
    )
    expect(fetch).toHaveBeenCalledWith(
      'https://api.telegram.org/bottelegram-token/sendMessage',
      expect.objectContaining({
        method: 'POST',
      }),
    )
    expect(ack).toHaveBeenCalled()
    expect(retry).not.toHaveBeenCalled()
  })

  it('acks malformed jobs without retrying', async () => {
    const diagnostic = vi.spyOn(console, 'warn').mockImplementation(() => undefined)
    const ack = vi.fn()
    const retry = vi.fn()
    await handleAiTelegramMessage(
      {
        messages: [
          { body: { tenantId: 'tenant-1', message: 'missing chat id' }, ack, retry },
        ],
      } as never,
      {} as never,
    )

    expect(ack).toHaveBeenCalled()
    expect(retry).not.toHaveBeenCalled()
    expect(diagnostic).toHaveBeenCalledWith('[ai-telegram] Malformed job, discarding:', {
      tenantId: 'tenant-1',
      message: 'missing chat id',
    })
    diagnostic.mockRestore()
  })
})
