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

vi.mock('resend', () => ({ Resend: vi.fn() }))
vi.mock('twilio', () => ({ default: vi.fn(() => ({ messages: { create: vi.fn() } })) }))
vi.mock('firebase-admin', () => ({
  default: {
    apps: [],
    initializeApp: vi.fn(),
    credential: { cert: vi.fn() },
    messaging: vi.fn(() => ({ send: vi.fn() })),
  },
}))
vi.mock('../lib/prisma', () => ({
  prisma: {
    notification: {
      create: vi.fn(),
      update: vi.fn(),
    },
  },
}))
vi.mock('../lib/redis', () => ({
  redis: { publish: vi.fn(), on: vi.fn(), get: vi.fn(), set: vi.fn(), del: vi.fn(), quit: vi.fn() },
}))
vi.mock('./notificationLocalizationService', async () => {
  const actual = await vi.importActual<typeof import('./notificationLocalizationService')>('./notificationLocalizationService')
  return {
    ...actual,
    resolveNotificationLocale: vi.fn().mockResolvedValue('en'),
    resolveNotificationTemplate: vi.fn(),
  }
})

import { prisma } from '../lib/prisma'
import { redis } from '../lib/redis'
import { sendNotification } from './notificationService'
import { resolveNotificationLocale, resolveNotificationTemplate } from './notificationLocalizationService'

describe('notificationService delivery boundaries', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    vi.mocked(prisma.notification.create).mockResolvedValue({ id: 'notification_1', title: 'Hello', body: 'Body' } as never)
    vi.mocked(prisma.notification.update).mockResolvedValue({ id: 'notification_1' } as never)
    vi.mocked(resolveNotificationLocale).mockResolvedValue('en')
    vi.mocked(resolveNotificationTemplate).mockResolvedValue(null as never)
  })

  it('persists and publishes in-app notifications for employee recipients', async () => {
    const result = await sendNotification({
      type: 'SYSTEM_ALERT' as never,
      title: 'Maintenance window',
      body: 'The dashboard will be unavailable.',
      companyId: 'company_1',
      employeeId: 'employee_1',
      channels: ['IN_APP' as never],
      data: { severity: 'low' },
    })

    expect(result).toEqual([{ channel: 'IN_APP', success: true }])
    expect(resolveNotificationLocale).toHaveBeenCalledWith(expect.objectContaining({
      companyId: 'company_1',
      employeeId: 'employee_1',
    }))
    expect(prisma.notification.create).toHaveBeenCalledWith({
      data: expect.objectContaining({
        type: 'SYSTEM_ALERT',
        title: 'Maintenance window',
        body: 'The dashboard will be unavailable.',
        channel: 'IN_APP',
        status: 'PENDING',
        companyId: 'company_1',
        employeeId: 'employee_1',
        renterId: null,
      }),
    })
    expect(redis.publish).toHaveBeenCalledWith(
      'notifications:employee_1',
      expect.stringContaining('"status":"DELIVERED"'),
    )
    expect(prisma.notification.update).toHaveBeenCalledWith({
      where: { id: 'notification_1' },
      data: expect.objectContaining({ status: 'SENT', providerMessageId: null }),
    })
  })

  it('uses resolved templates and records failed channels without throwing', async () => {
    vi.mocked(resolveNotificationTemplate).mockResolvedValue({
      templateKey: 'reservation.confirmed',
      locale: 'fr',
      subject: 'Réservation confirmée',
      body: 'Votre réservation est confirmée.',
      usedFallback: false,
      version: 2,
    } as never)

    const result = await sendNotification({
      type: 'RESERVATION_UPDATE' as never,
      templateKey: 'reservation.confirmed',
      templateVariables: { firstName: 'Aya' },
      companyId: 'company_1',
      channels: ['EMAIL' as never],
      email: 'aya@example.test',
    })

    expect(result).toEqual([{ channel: 'EMAIL', success: false, error: 'No email provider is configured' }])
    expect(resolveNotificationTemplate).toHaveBeenCalledWith(expect.objectContaining({
      templateKey: 'reservation.confirmed',
      channel: 'EMAIL',
      locale: 'en',
      variables: { firstName: 'Aya' },
    }))
    expect(prisma.notification.create).toHaveBeenCalledWith({
      data: expect.objectContaining({
        title: 'Réservation confirmée',
        body: 'Votre réservation est confirmée.',
        templateKey: 'reservation.confirmed',
        locale: 'fr',
      }),
    })
    expect(prisma.notification.update).not.toHaveBeenCalled()
  })

  it('fails incomplete notification content before persistence', async () => {
    const result = await sendNotification({
      type: 'SYSTEM_ALERT' as never,
      title: 'Missing body',
      channels: ['IN_APP' as never],
      employeeId: 'employee_1',
    })

    expect(result).toEqual([{ channel: 'IN_APP', success: false, error: 'Notification content is incomplete for channel IN_APP' }])
    expect(prisma.notification.create).not.toHaveBeenCalled()
    expect(redis.publish).not.toHaveBeenCalled()
  })
})
