import React, { useContext } from 'react'
import type { Event, UserResponse } from 'stream-chat'
import {
  AIStates,
  useAIState,
  useChannelStateContext,
  useChatContext,
  useTypingContext,
} from 'stream-chat-react'

import { Avatar } from '../Avatar'
import { MessageTail } from '../CustomMessage/MessageTail'

import { DmAgentEnabledContext } from './DmAgentContext'

interface CustomTypingIndicatorProps {
  threadList?: boolean
}

const Circle = ({ cx, index }: { cx: string; index: number }) => (
  <circle cx={cx} cy="6.15" r="3.9" fill="#A0A0A0">
    <animateTransform
      attributeName="transform"
      type="translate"
      values="0 0; 0 -2.25; 0 0;"
      dur="900ms"
      begin={`${120 * index}ms`} // 0ms, 120ms, 240ms
      repeatCount="indefinite"
    />
  </circle>
)

const AI_ACTIVE_STATES = new Set<string>([
  AIStates.Thinking,
  AIStates.Generating,
  AIStates.ExternalSources,
])

const CustomTypingIndicator = ({ threadList }: CustomTypingIndicatorProps) => {
  const { channel, channelConfig, thread } = useChannelStateContext()
  const { client } = useChatContext()
  const { typing = {} } = useTypingContext()
  const { aiState } = useAIState(channel)
  const dmAgentEnabled = useContext(DmAgentEnabledContext)

  // Show the AI indicator whenever the consumer agent is producing a reply.
  // This event stream is independent of `typing.start`/`typing.stop`, so it is
  // intentionally NOT gated by `channelConfig.typing_events`. Gate strictly on
  // `dmAgentEnabled` so stale or off-surface ai_indicator events never surface
  // the bubble on channels where the agent is not active.
  const isAiActive =
    !threadList && dmAgentEnabled && AI_ACTIVE_STATES.has(aiState)

  if (isAiActive) {
    const agentUser = findOtherChannelUser(channel, client.user?.id)
    return (
      <TypingBubble
        avatarId={agentUser?.id ?? 'ai-agent'}
        avatarName={agentUser?.name ?? agentUser?.id ?? 'Agent'}
        avatarImage={agentUser?.image}
        testId="typing-indicator-ai"
      />
    )
  }

  if (channelConfig?.typing_events === false) {
    return null
  }

  const typingInChannel = !threadList
    ? Object.values(typing).filter(
        ({ parent_id, user }: Event) =>
          user?.id !== client.user?.id && !parent_id
      )
    : []

  const typingInThread = threadList
    ? Object.values(typing).filter(
        ({ parent_id, user }: Event) =>
          user?.id !== client.user?.id && parent_id === thread?.id
      )
    : []

  const typingUsers = threadList ? typingInThread : typingInChannel
  if (!typingUsers.length) {
    return null
  }

  const typingUser = typingUsers[0]?.user
  const memberUser =
    typingUser?.id && channel.state.members[typingUser.id]
      ? channel.state.members[typingUser.id].user
      : undefined

  return (
    <TypingBubble
      avatarId={typingUser?.id ?? memberUser?.id ?? 'typing-user'}
      avatarName={
        typingUser?.name ?? memberUser?.name ?? typingUser?.id ?? 'Typing user'
      }
      avatarImage={typingUser?.image ?? memberUser?.image}
      testId="typing-indicator"
    />
  )
}

const TypingBubble = ({
  avatarId,
  avatarName,
  avatarImage,
  testId,
}: {
  avatarId: string
  avatarName: string
  avatarImage?: string | null
  testId: string
}) => (
  <div
    className="str-chat__li str-chat__li--single str-chat__typing-indicator !static"
    data-testid={testId}
    style={{
      marginBottom: 'var(--messaging-channel-input-spacer-height, 80px)',
    }}
  >
    <div className="str-chat__message str-chat__message-simple str-chat__message--regular str-chat__message--received str-chat__message--other str-chat__message--has-text">
      <Avatar
        className="str-chat__avatar str-chat__message-sender-avatar"
        id={avatarId}
        name={avatarName}
        image={avatarImage ?? undefined}
        size={24}
        shape="circle"
      />
      <div className="str-chat__message-inner mx-0">
        <div className="str-chat__message-bubble-wrapper">
          <div className="str-chat__message-bubble relative isolate">
            <div className="str-chat__message-text min-h-11 flex items-center">
              <div className="str-chat__message-text-inner str-chat__message-simple-text-inner">
                <svg
                  aria-hidden="true"
                  className="block overflow-visible"
                  viewBox="0 0 32 12.3"
                  width="32"
                  height="13"
                  overflow="visible"
                >
                  <Circle cx="4" index={0} />
                  <Circle cx="16" index={1} />
                  <Circle cx="28" index={2} />
                </svg>
              </div>
            </div>
            <MessageTail side="other" />
          </div>
        </div>
      </div>
    </div>
  </div>
)

type ChannelLike = ReturnType<typeof useChannelStateContext>['channel']

function findOtherChannelUser(
  channel: ChannelLike,
  selfId: string | undefined
): UserResponse | undefined {
  const members = channel?.state?.members ?? {}
  for (const member of Object.values(members)) {
    const memberUser = member?.user
    if (memberUser && memberUser.id !== selfId) {
      return memberUser
    }
  }
  return undefined
}

export default CustomTypingIndicator
