import '../stream-custom-data'

import type { StoryFn } from '@storybook/react'
import React, { useEffect } from 'react'
import {
  Channel as ChannelType,
  QueryChannelAPIResponse,
  StreamChat,
} from 'stream-chat'
import {
  Channel,
  Chat,
  MessageList,
  MessageUIComponentProps,
  Window,
} from 'stream-chat-react'

import { CustomMessage } from '../components/CustomMessage'
import {
  OutboundContext,
  buildOutboundsByRecordId,
} from '../components/CustomMessage/OutboundContext'
import CustomTypingIndicator from '../components/CustomTypingIndicator'
import { createMockStreamChatClient } from '../testing/createMockStreamChatClient'
import type { OutboundResolver } from '../types'

import { minutesAgo } from './decorators/storyTime'
import { StoryUser, storyUsers } from './decorators/storyUser'

export const createMockChannel = async (
  client: StreamChat,
  messages: TemplateProps['messages']
) => {
  const mockMessages = messages.map((msg, index) => ({
    ...msg,
    type: msg.type ?? ('regular' as const),
    created_at: minutesAgo(messages.length - index),
    updated_at: minutesAgo(messages.length - index),
    attachments: msg.attachments ?? [],
    latest_reactions: [],
    own_reactions: [],
    reaction_counts: {},
    reaction_scores: {},
    reply_count: 0,
    status: 'received',
    cid: 'messaging:storybook-channel-1',
    mentioned_users: msg.mentioned_users ?? [],
  }))

  const channelData = {
    members: [storyUsers.creator.id, storyUsers.visitor.id],
    // Required for useUserRole: canDelete needs delete-own-message, canFlag needs flag-message
    own_capabilities: ['delete-own-message', 'flag-message'],
  }

  const channel = client.channel(
    'messaging',
    'storybook-channel-1',
    channelData
  )

  channel.watch = async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    channel.state.messages = mockMessages as unknown as any[]
    channel.state.members = {
      [storyUsers.creator.id]: {
        user: storyUsers.creator,
        user_id: storyUsers.creator.id,
      },
      [storyUsers.visitor.id]: {
        user: storyUsers.visitor,
        user_id: storyUsers.visitor.id,
      },
    }
    return {
      channel: channelData,
      members: [],
      messages: mockMessages,
      watchers: [],
      pinned_messages: [],
      duration: '0ms',
    } as unknown as QueryChannelAPIResponse
  }

  try {
    await channel.watch()
  } catch {
    // Ignore errors in mock mode
  }

  return channel
}

export interface TemplateProps {
  currentUser: StoryUser
  typingUser?: StoryUser
  resolveOutbound?: OutboundResolver
  messages: Array<{
    id: string
    text: string
    user: StoryUser
    mentioned_users?: StoryUser[]
    type?: 'regular' | 'system'
    attachments?: Array<Record<string, unknown>>
    metadata?: {
      custom_type?:
        | 'MESSAGE_TIP'
        | 'MESSAGE_PAID'
        | 'MESSAGE_CHATBOT'
        | 'MESSAGE_ATTACHMENT'
        | 'MESSAGE_OUTBOUND'
      payment_status?: 'pending' | 'paid' | 'failed' | 'refunded'
      amount_text?: string
      attachment_title?: string
      attachment_mime_type?: string
      attachment_thumbnail?: string
      attachment_detail?: string
      attachment_content_type?: 'text' | 'media'
      outbound_id?: string
    }
  }>
}

const TemplateInner: React.FC<{
  currentUser: StoryUser
  typingUser?: StoryUser
  messages: TemplateProps['messages']
  resolveOutbound?: OutboundResolver
}> = ({ currentUser, typingUser, messages, resolveOutbound }) => {
  const [client] = React.useState(() => createMockStreamChatClient(currentUser))

  const [channel, setChannel] = React.useState<ChannelType | null>(null)

  useEffect(() => {
    createMockChannel(client, messages).then(setChannel)
  }, [client, messages])

  useEffect(() => {
    if (!channel || !typingUser) {
      return
    }

    // Stream Channel UI only updates typing context from typing.start/typing.stop events.
    const timer = setTimeout(() => {
      client.dispatchEvent({
        type: 'typing.start',
        cid: channel.cid,
        user: typingUser,
      })
    }, 0)

    return () => clearTimeout(timer)
  }, [channel, client, typingUser])

  const outboundsByRecordId = React.useMemo(
    () => buildOutboundsByRecordId(messages, resolveOutbound),
    [messages, resolveOutbound]
  )

  const MessageComponent = React.useMemo(() => {
    return function CustomMessageComponent(props: MessageUIComponentProps) {
      return <CustomMessage {...props} onOutboundClick={() => undefined} />
    }
  }, [])

  if (!channel) {
    return <div className="p-4">Loading...</div>
  }

  return (
    <OutboundContext.Provider value={outboundsByRecordId}>
      <Chat client={client}>
        <div className="h-screen w-full bg-white">
          <Channel
            channel={channel}
            Message={MessageComponent}
            TypingIndicator={CustomTypingIndicator}
          >
            <Window>
              <MessageList />
            </Window>
          </Channel>
        </div>
      </Chat>
    </OutboundContext.Provider>
  )
}

export const Template: StoryFn<TemplateProps> = ({
  currentUser = storyUsers.creator,
  typingUser,
  messages,
  resolveOutbound,
}) => (
  <TemplateInner
    key={currentUser.id}
    currentUser={currentUser}
    typingUser={typingUser}
    messages={messages}
    resolveOutbound={resolveOutbound}
  />
)
