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

import type { DeliverableNotification } from '@zync/types'

const fetchMock = vi.fn()
const sendWebPushMock = vi.fn()
const loadAdapterCredentialMock = vi.fn()
const getUserTelegramPrefsMock = vi.fn()
const getUserTelegramChatIdMock = vi.fn()
const getPushSubscriptionsForUserMock = vi.fn()
const countPushSubscriptionsForUserMock = vi.fn()
const getNotificationPreferencesMock = vi.fn()

vi.stubGlobal('fetch', fetchMock)

vi.mock('../../../../../../packages/notifications/src/adapters/email', () => ({
  EmailNotificationAdapter: class {
    readonly id = 'email'
    async canDeliver() {
      return false
    }
    async deliver() {
      return { delivered: false }
    }
  },
}))

vi.mock('@zync/db/queries', async () => {
  const actual = await vi.importActual<object>('@zync/db/queries')
  return {
    ...actual,
    countPushSubscriptionsForUser: countPushSubscriptionsForUserMock,
    getNotificationPreferences: getNotificationPreferencesMock,
    getPushSubscriptionsForUser: getPushSubscriptionsForUserMock,
    getUserTelegramChatId: getUserTelegramChatIdMock,
    getUserTelegramPrefs: getUserTelegramPrefsMock,
  }
})

vi.mock('../../../../../../packages/notifications/src/credentials', () => ({
  loadAdapterCredential: loadAdapterCredentialMock,
}))

vi.mock('../../../../../../packages/notifications/src/web-push/send', async () => {
  const actual = await vi.importActual<object>('../../../../../../packages/notifications/src/web-push/send')
  return {
    ...actual,
    sendWebPush: sendWebPushMock,
  }
})

describe('notifications platform parity', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    fetchMock.mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 }))
    loadAdapterCredentialMock.mockResolvedValue('telegram-token')
    getUserTelegramPrefsMock.mockResolvedValue({ telegramTypes: ['invoice_sent'] })
    getUserTelegramChatIdMock.mockResolvedValue('123456')
    getPushSubscriptionsForUserMock.mockResolvedValue([
      { endpoint: 'https://push.example.test/sub-1', p256dh: 'p256dh', auth: 'auth' },
    ])
    countPushSubscriptionsForUserMock.mockResolvedValue(1)
    getNotificationPreferencesMock.mockResolvedValue({
      email: {},
      inApp: { invoicePaid: true },
      digest: 'daily',
    })
    sendWebPushMock.mockResolvedValue({
      endpoint: 'https://push.example.test/sub-1',
      status: 201,
      gone: false,
    })
  })

  it('matches the legacy telegram request body and deduplicates the second send', async () => {
    const { TelegramNotificationAdapter } = await import('../../../../../../packages/notifications/src/adapters/telegram')
    const { createPlatformNotificationDeps } = await import('../notifications')

    const notification: DeliverableNotification = {
      type: 'invoice_sent',
      title: 'Invoice <42>',
      body: 'Paid & settled',
      entityType: 'invoices',
      entityId: 'inv-42',
      actionButtons: [{ label: 'Open', url: 'https://app.zync.is/invoices/inv-42' }],
    }

    let seen = false
    const db = {
      execute: vi.fn(async () => {
        if (!seen) {
          seen = true
          return []
        }
        return [{ key: 'notifications:invoice_sent:invoices:inv-42:user-1' }]
      }),
    }
    const env = {
      INTEGRATION_ENCRYPTION_KEY: 'encrypt-key',
      VAPID_PRIVATE_KEY: 'vapid-private',
      VAPID_PUBLIC_KEY: 'vapid-public',
    }

    const legacy = new TelegramNotificationAdapter({} as never, env as never, 'tenant-1')
    await legacy.deliver('user-1', notification)
    const legacyBody = JSON.parse(fetchMock.mock.calls[0]?.[1]?.body as string)

    const deps = createPlatformNotificationDeps(db as never, env as never, 'tenant-1')
    await deps.deliver('user-1', notification)
    await deps.deliver('user-1', notification)

    const platformBody = JSON.parse(fetchMock.mock.calls[1]?.[1]?.body as string)

    expect(platformBody).toEqual(legacyBody)
    expect(fetchMock).toHaveBeenCalledTimes(2)
  })

  it('matches the legacy web-push payload and sends only one provider call on a duplicate retry', async () => {
    const { WebPushNotificationAdapter } = await import('../../../../../../packages/notifications/src/adapters/web-push')
    const { createPlatformNotificationDeps } = await import('../notifications')

    const notification: DeliverableNotification = {
      type: 'invoice_paid',
      title: 'Invoice paid',
      body: 'Customer paid invoice INV-99',
      entityType: 'invoices',
      entityId: 'inv-99',
    }

    let seen = false
    const db = {
      execute: vi.fn(async () => {
        if (!seen) {
          seen = true
          return []
        }
        return [{ key: 'notifications:invoice_paid:invoices:inv-99:user-1' }]
      }),
    }
    const env = {
      INTEGRATION_ENCRYPTION_KEY: 'encrypt-key',
      VAPID_PRIVATE_KEY: 'vapid-private',
      VAPID_PUBLIC_KEY: 'vapid-public',
    }

    const legacy = new WebPushNotificationAdapter({} as never, env as never, 'tenant-1')
    await legacy.deliver('user-1', notification)
    const legacyArgs = sendWebPushMock.mock.calls[0]

    const deps = createPlatformNotificationDeps(db as never, env as never, 'tenant-1')
    await deps.deliver('user-1', notification)
    await deps.deliver('user-1', notification)

    const platformArgs = sendWebPushMock.mock.calls[1]

    expect(platformArgs).toEqual(legacyArgs)
    expect(sendWebPushMock).toHaveBeenCalledTimes(2)
  })
})
