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

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

import { DmAgentEnabledContext } from './DmAgentContext'

const visitor = { id: 'visitor-1', name: 'Visitor' }
const agent = { id: 'creator-1', name: 'Creator', image: 'agent.png' }

let typingContext: { typing: Record<string, Event> } = { typing: {} }
let aiStateContext: { aiState: string } = { aiState: 'AI_STATE_IDLE' }
let channelStateContext: {
  channel: {
    state: { members: Record<string, { user: typeof visitor | typeof agent }> }
  }
  channelConfig: { typing_events: boolean }
  thread: undefined
} = {
  channel: {
    state: {
      members: {
        [visitor.id]: { user: visitor },
        [agent.id]: { user: agent },
      },
    },
  },
  channelConfig: { typing_events: true },
  thread: undefined,
}

vi.mock('stream-chat-react', () => ({
  AIStates: {
    Error: 'AI_STATE_ERROR',
    ExternalSources: 'AI_STATE_EXTERNAL_SOURCES',
    Generating: 'AI_STATE_GENERATING',
    Idle: 'AI_STATE_IDLE',
    Stop: 'AI_STATE_STOP',
    Thinking: 'AI_STATE_THINKING',
  },
  useAIState: () => aiStateContext,
  useChannelStateContext: () => channelStateContext,
  useChatContext: () => ({ client: { user: visitor } }),
  useTypingContext: () => typingContext,
}))

vi.mock('../Avatar', () => ({
  Avatar: ({ name, id }: { name: string; id: string }) => (
    <div data-testid="avatar" data-id={id}>
      {name}
    </div>
  ),
}))

const importIndicator = async () => (await import('.')).default

const renderIndicator = async (dmAgentEnabled = true) => {
  const CustomTypingIndicator = await importIndicator()
  return renderWithProviders(
    <DmAgentEnabledContext.Provider value={dmAgentEnabled}>
      <CustomTypingIndicator />
    </DmAgentEnabledContext.Provider>
  )
}

describe('CustomTypingIndicator', () => {
  beforeEach(() => {
    typingContext = { typing: {} }
    aiStateContext = { aiState: 'AI_STATE_IDLE' }
    channelStateContext = {
      channel: {
        state: {
          members: {
            [visitor.id]: { user: visitor },
            [agent.id]: { user: agent },
          },
        },
      },
      channelConfig: { typing_events: true },
      thread: undefined,
    }
  })

  it('renders nothing when idle and no typers', async () => {
    await renderIndicator()
    expect(screen.queryByTestId('typing-indicator')).toBeNull()
    expect(screen.queryByTestId('typing-indicator-ai')).toBeNull()
  })

  it('renders the human typing bubble when someone else is typing', async () => {
    typingContext = {
      typing: {
        [agent.id]: {
          type: 'typing.start',
          user: agent,
          parent_id: undefined,
        } as Event,
      },
    }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator')).toBeInTheDocument()
    expect(screen.getByTestId('avatar')).toHaveAttribute('data-id', agent.id)
  })

  it('hides the human typing bubble when typing_events is disabled', async () => {
    channelStateContext.channelConfig.typing_events = false
    typingContext = {
      typing: {
        [agent.id]: {
          type: 'typing.start',
          user: agent,
          parent_id: undefined,
        } as Event,
      },
    }
    await renderIndicator()
    expect(screen.queryByTestId('typing-indicator')).toBeNull()
  })

  it('renders the AI bubble when the agent is thinking', async () => {
    aiStateContext = { aiState: 'AI_STATE_THINKING' }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator-ai')).toBeInTheDocument()
    expect(screen.getByTestId('avatar')).toHaveAttribute('data-id', agent.id)
  })

  it('renders the AI bubble when the agent is generating', async () => {
    aiStateContext = { aiState: 'AI_STATE_GENERATING' }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator-ai')).toBeInTheDocument()
  })

  it('renders the AI bubble when the agent is checking external sources', async () => {
    aiStateContext = { aiState: 'AI_STATE_EXTERNAL_SOURCES' }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator-ai')).toBeInTheDocument()
  })

  it('does not render the AI bubble inside a thread list', async () => {
    aiStateContext = { aiState: 'AI_STATE_GENERATING' }
    const CustomTypingIndicator = await importIndicator()
    renderWithProviders(
      <DmAgentEnabledContext.Provider value={true}>
        <CustomTypingIndicator threadList />
      </DmAgentEnabledContext.Provider>
    )
    expect(screen.queryByTestId('typing-indicator-ai')).toBeNull()
  })

  it('renders the AI bubble even when typing_events is disabled', async () => {
    channelStateContext.channelConfig.typing_events = false
    aiStateContext = { aiState: 'AI_STATE_THINKING' }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator-ai')).toBeInTheDocument()
  })

  it('falls back to a default avatar id when no other channel member exists', async () => {
    channelStateContext.channel.state.members = {
      [visitor.id]: { user: visitor },
    }
    aiStateContext = { aiState: 'AI_STATE_GENERATING' }
    await renderIndicator()
    expect(screen.getByTestId('typing-indicator-ai')).toBeInTheDocument()
    expect(screen.getByTestId('avatar')).toHaveAttribute('data-id', 'ai-agent')
  })

  it('does not render the AI bubble when dmAgentEnabled is false', async () => {
    aiStateContext = { aiState: 'AI_STATE_GENERATING' }
    await renderIndicator(false)
    expect(screen.queryByTestId('typing-indicator-ai')).toBeNull()
  })

  it('still renders the human bubble when dmAgentEnabled is false', async () => {
    typingContext = {
      typing: {
        [agent.id]: {
          type: 'typing.start',
          user: agent,
          parent_id: undefined,
        } as Event,
      },
    }
    await renderIndicator(false)
    expect(screen.getByTestId('typing-indicator')).toBeInTheDocument()
  })
})
