import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { NotificationBell } from '@/components/layout/NotificationBell'

vi.mock('@/components/ui/Toast', () => ({
  ToastProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  useToast: () => ({ toast: vi.fn() }),
}))

vi.mock('@/components/providers/BusinessProvider', () => ({
  useBusiness: () => ({
    business: { businessId: '11111111-1111-1111-1111-111111111111', businessName: 'Demo Biz', userId: 'u1', userEmail: 'demo@imbaraga.rw' },
    refresh: vi.fn(),
  }),
}))

const getNotifications = vi.fn()
const markNotificationRead = vi.fn().mockResolvedValue({ success: true, data: { id: 'n-1' } })
const markAllNotificationsRead = vi.fn().mockResolvedValue({ success: true, data: { updated: true } })

vi.mock('@/lib/api/agent', async importOriginal => {
  const actual = await importOriginal<typeof import('@/lib/api/agent')>()
  return {
    ...actual,
    getNotifications: (...args: unknown[]) => getNotifications(...args),
    markNotificationRead: (...args: unknown[]) => markNotificationRead(...args),
    markAllNotificationsRead: (...args: unknown[]) => markAllNotificationsRead(...args),
  }
})

function makeNotification(overrides: Partial<Record<string, unknown>> = {}) {
  return {
    id: 'n-1',
    business_id: '11111111-1111-1111-1111-111111111111',
    customer_id: null,
    task_id: null,
    type: 'order_created',
    recipient: '+250788000001',
    message: 'Order received for Jean',
    status: 'sent',
    sent_at: '2026-08-21T10:00:00Z',
    created_at: '2026-08-21T10:00:00Z',
    read_at: null,
    ...overrides,
  }
}

beforeEach(() => {
  vi.clearAllMocks()
})

describe('NotificationBell', () => {
  it('shows unread count badge when notifications are unread', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: {
        notifications: [makeNotification(), makeNotification({ id: 'n-2', message: 'Second', read_at: '2026-08-21T11:00:00Z' })],
        unreadCount: 1,
      },
    })
    render(<NotificationBell />)
    await waitFor(() => expect(screen.getByText('1')).toBeInTheDocument())
    expect(getNotifications).toHaveBeenCalledWith({ limit: 20 })
  })

  it('renders no badge when everything is read', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: { notifications: [makeNotification({ read_at: '2026-08-21T11:00:00Z' })], unreadCount: 0 },
    })
    render(<NotificationBell />)
    await waitFor(() => expect(screen.getByLabelText('Notifications')).toBeInTheDocument())
    expect(screen.queryByText('9+')).not.toBeInTheDocument()
  })

  it('lists messages in the dropdown and marks one read on click', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: { notifications: [makeNotification()], unreadCount: 1 },
    })
    render(<NotificationBell />)
    await waitFor(() => expect(screen.getByText('1')).toBeInTheDocument())

    fireEvent.click(screen.getByLabelText('Notifications'))
    expect(screen.getByText('Order received for Jean')).toBeInTheDocument()
    expect(screen.getByText('Mark all read')).toBeInTheDocument()

    fireEvent.click(screen.getByText('Order received for Jean'))
    await waitFor(() => expect(markNotificationRead).toHaveBeenCalledWith('n-1'))
    await waitFor(() => expect(screen.queryByText('Mark all read')).not.toBeInTheDocument())
  })

  it('marks all as read via the header action', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: {
        notifications: [makeNotification(), makeNotification({ id: 'n-2', message: 'Shipped to Jean' })],
        unreadCount: 2,
      },
    })
    render(<NotificationBell />)
    await waitFor(() => expect(screen.getByText('2')).toBeInTheDocument())

    fireEvent.click(screen.getByLabelText('Notifications'))
    fireEvent.click(screen.getByText('Mark all read'))

    await waitFor(() => expect(markAllNotificationsRead).toHaveBeenCalledTimes(1))
    await waitFor(() => expect(screen.queryByText('Mark all read')).not.toBeInTheDocument())
  })

  it('shows empty state text when there are no notifications', async () => {
    getNotifications.mockResolvedValue({ success: true, data: { notifications: [], unreadCount: 0 } })
    render(<NotificationBell />)
    fireEvent.click(await screen.findByLabelText('Notifications'))
    await waitFor(() => expect(screen.getByText('No notifications yet')).toBeInTheDocument())
  })

  it('links low-stock alerts to the inventory page', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: {
        notifications: [
          makeNotification({ id: 'n-3', type: 'low_stock', message: 'Wireless Mouse is running low' }),
          makeNotification({ id: 'n-4', message: 'Order received for Jean' }),
        ],
        unreadCount: 2,
      },
    })
    render(<NotificationBell />)
    await waitFor(() => expect(screen.getByText('2')).toBeInTheDocument())

    fireEvent.click(screen.getByLabelText('Notifications'))
    const alertLink = screen.getByText('Wireless Mouse is running low').closest('a')
    expect(alertLink).toHaveAttribute('href', '/dashboard/inventory')
    // non-alert notifications remain plain buttons
    expect(screen.getByText('Order received for Jean').closest('a')).toBeNull()
  })

  it('marks a stock alert read when its link is clicked', async () => {
    getNotifications.mockResolvedValue({
      success: true,
      data: {
        notifications: [makeNotification({ id: 'n-5', type: 'out_of_stock', message: 'Wireless Mouse is OUT OF STOCK' })],
        unreadCount: 1,
      },
    })
    render(<NotificationBell />)
    fireEvent.click(await screen.findByLabelText('Notifications'))
    fireEvent.click(screen.getByText('Wireless Mouse is OUT OF STOCK'))
    await waitFor(() => expect(markNotificationRead).toHaveBeenCalledWith('n-5'))
  })
})
