import React from 'react'
import { useChannelStateContext, useMessageContext } from 'stream-chat-react'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { renderWithProviders, screen } from '../../test/utils'

import { OutboundContext } from './OutboundContext'
import { isTipMessage } from './TipMessage'

import { CustomMessage } from './index'

vi.mock('stream-chat-react', () => ({
  Attachment: () => null,
  EditMessageModal: () => null,
  MessageBounceModal: () => null,
  MessageBouncePrompt: () => null,
  MessageBlocked: () => null,
  MessageDeleted: () => null,
  MessageErrorIcon: () => null,
  MessageIsThreadReplyInChannelButtonIndicator: () => null,
  MessageRepliesCountButton: () => null,
  MessageStatus: ({
    MessageSentStatus,
  }: {
    MessageSentStatus: React.ComponentType
  }) => <MessageSentStatus />,
  MessageText: () => <span>Message</span>,
  Poll: () => null,
  ReminderNotification: () => null,
  StreamedMessageText: () => null,
  areMessageUIPropsEqual: () => true,
  isDateSeparatorMessage: () => false,
  isMessageBlocked: () => false,
  isMessageBounced: () => false,
  messageHasAttachments: () => false,
  messageHasReactions: () => false,
  useChannelStateContext: vi.fn(),
  useChatContext: () => ({ client: { polls: { fromState: vi.fn() } } }),
  useComponentContext: () => ({
    Attachment: () => null,
    MessageRepliesCountButton: () => null,
  }),
  useMessageContext: vi.fn(),
  useMessageReminder: () => undefined,
}))

vi.mock('./context', () => ({
  useCustomMessage: () => ({
    isUnlocking: () => false,
  }),
}))

vi.mock('../Avatar', () => ({
  Avatar: () => null,
}))

vi.mock('./MessageTag', () => ({
  MessageTag: () => null,
  isAttachmentMessage: () => false,
  isChatbotMessage: () => false,
  isMediaAttachmentMessage: () => false,
  isPaidMessage: () => false,
  isTextAttachmentMessage: () => false,
  hasPaidDeliveryTag: () => false,
}))

vi.mock('./TipMessage', () => ({
  TipMessage: () => null,
  isTipMessage: vi.fn(() => false),
}))

vi.mock('./LockedAttachment', () => ({
  default: () => null,
}))

vi.mock('./StreamAttachmentMessage', () => ({
  default: () => null,
  buildOrderedAttachmentSegments: () => [],
}))

vi.mock('../MediaMessage', () => ({
  isLinkAttachment: () => false,
}))

const mockedUseChannelStateContext = vi.mocked(useChannelStateContext)
const mockedUseMessageContext = vi.mocked(useMessageContext)
const mockedIsTipMessage = vi.mocked(isTipMessage)

describe('CustomMessage', () => {
  beforeEach(() => {
    mockedIsTipMessage.mockReturnValue(false)
    mockedUseChannelStateContext.mockReturnValue({
      messages: [{ id: 'message-1', user: { id: 'me' } }],
    } as never)
    mockedUseMessageContext.mockReturnValue({
      message: {
        id: 'message-1',
        text: 'Hello',
        type: 'regular',
        status: 'received',
        user: { id: 'me', name: 'Me' },
        metadata: {
          outbound_id: 'outbound-1',
        },
      },
      isMyMessage: () => true,
      threadList: false,
      editing: false,
      endOfGroup: true,
      firstOfGroup: true,
      groupedByUser: true,
      handleAction: vi.fn(),
      handleOpenThread: vi.fn(),
      handleRetry: vi.fn(),
      highlighted: false,
      renderText: undefined,
      isMessageAIGenerated: undefined,
    } as never)
  })

  it('uses the latest outbound click handler after rerendering', () => {
    const firstHandler = vi.fn()
    const secondHandler = vi.fn()
    const { rerender } = renderWithProviders(
      <OutboundContext.Provider
        value={{
          'outbound-1': {
            id: 'automation-1',
            name: 'July 9th Class attendees',
          },
        }}
      >
        <CustomMessage onOutboundClick={firstHandler} />
      </OutboundContext.Provider>
    )

    rerender(
      <OutboundContext.Provider
        value={{
          'outbound-1': {
            id: 'automation-1',
            name: 'July 9th Class attendees',
          },
        }}
      >
        <CustomMessage onOutboundClick={secondHandler} />
      </OutboundContext.Provider>
    )
    screen
      .getByRole('button', {
        name: 'View broadcast July 9th Class attendees',
      })
      .click()

    expect(firstHandler).not.toHaveBeenCalled()
    expect(secondHandler).toHaveBeenCalledWith({
      id: 'automation-1',
      name: 'July 9th Class attendees',
    })
  })

  it('renders the current source returned for the outbound id', () => {
    renderWithProviders(
      <OutboundContext.Provider
        value={{
          'outbound-1': {
            id: 'automation-1',
            name: 'July 9th Class absentee',
          },
        }}
      >
        <CustomMessage />
      </OutboundContext.Provider>
    )

    expect(
      screen.getByTestId('sent-message-outbound-status')
    ).toHaveTextContent('July 9th Class absentee')
  })

  it('hides the label when the outbound record cannot be resolved', () => {
    renderWithProviders(
      <OutboundContext.Provider value={{}}>
        <CustomMessage />
      </OutboundContext.Provider>
    )

    expect(
      screen.queryByTestId('sent-message-outbound-status')
    ).not.toBeInTheDocument()
  })

  it('renders an outbound source when its lookup later resolves', () => {
    const { rerender } = renderWithProviders(
      <OutboundContext.Provider value={{}}>
        <CustomMessage />
      </OutboundContext.Provider>
    )

    rerender(
      <OutboundContext.Provider
        value={{
          'outbound-1': {
            id: 'automation-1',
            name: 'July 9th Class absentee',
          },
        }}
      >
        <CustomMessage />
      </OutboundContext.Provider>
    )

    expect(
      screen.getByTestId('sent-message-outbound-status')
    ).toHaveTextContent('July 9th Class absentee')
  })

  it('does not render an accompanying text bubble for a whitespace-only tip message', () => {
    mockedIsTipMessage.mockReturnValue(true)
    mockedUseMessageContext.mockReturnValue({
      message: {
        id: 'message-1',
        text: '   \n  ',
        type: 'regular',
        status: 'received',
        user: { id: 'me', name: 'Me' },
        metadata: {},
      },
      isMyMessage: () => true,
      threadList: false,
      editing: false,
      endOfGroup: true,
      firstOfGroup: true,
      groupedByUser: true,
      handleAction: vi.fn(),
      handleOpenThread: vi.fn(),
      handleRetry: vi.fn(),
      highlighted: false,
      renderText: undefined,
      isMessageAIGenerated: undefined,
    } as never)

    renderWithProviders(
      <OutboundContext.Provider value={{}}>
        <CustomMessage />
      </OutboundContext.Provider>
    )

    expect(screen.queryByText('Message')).not.toBeInTheDocument()
  })

  it('renders an accompanying text bubble for a tip message with real text', () => {
    mockedIsTipMessage.mockReturnValue(true)
    // Default mocked message.text ('Hello') from beforeEach.

    renderWithProviders(
      <OutboundContext.Provider value={{}}>
        <CustomMessage />
      </OutboundContext.Provider>
    )

    expect(screen.getByText('Message')).toBeInTheDocument()
  })
})
