import type { Meta, StoryFn } from '@storybook/react'
import React, { useEffect } from 'react'
import {
  Channel as ChannelType,
  ChannelMemberResponse,
  Event,
  QueryChannelAPIResponse,
  StreamChat,
} from 'stream-chat'
import { Chat } from 'stream-chat-react'

import { hoursAgo, minutesAgo, now } from '../stories/decorators/storyTime'
import { mockParticipants } from '../stories/mocks'
import { createMockStreamChatClient } from '../testing/createMockStreamChatClient'

import { ChannelView } from './ChannelView'
import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'

type ComponentProps = React.ComponentProps<typeof ChannelView>
const SCROLLABLE_MESSAGE_COUNT = 10

const meta: Meta<ComponentProps> = {
  title: 'ChannelView',
  component: ChannelView,
  parameters: {
    layout: 'fullscreen',
    viewport: {
      defaultViewport: 'responsive',
    },
  },
}
export default meta

// Mock user for Storybook
const mockUser = {
  id: 'storybook-user',
  name: 'Storybook User',
  image: 'https://i.pravatar.cc/150?img=1',
}

const FloatingComposerInput: React.FC = () => (
  <div className="w-full rounded-[1.5rem] bg-white p-4 shadow-[0_4px_16px_0_rgba(0,0,0,0.08)]">
    <span className="text-sm text-black/40">Redesigned composer</span>
  </div>
)

// Create a real channel using client.channel() and mock the API responses
const createMockChannel = async (
  client: StreamChat,
  hasMessages = true,
  followerStatus?: string | boolean,
  isFrozen = false,
  channelCustomerTag?: string,
  hasScrollableMessages = false
) => {
  const participant = mockParticipants[0]

  const mockMessages = hasMessages
    ? [
        {
          id: 'msg-1',
          text: 'Hey! How are you doing?',
          type: 'regular' as const,
          created_at: hoursAgo(1),
          updated_at: hoursAgo(1),
          user: participant,
          html: '<p>Hey! How are you doing?</p>',
          attachments: [],
          latest_reactions: [],
          own_reactions: [],
          reaction_counts: {},
          reaction_scores: {},
          reply_count: 0,
          status: 'received',
          cid: 'messaging:storybook-channel-1',
          mentioned_users: [],
        },
        {
          id: 'msg-2',
          text: "I'm doing great, thanks! How about you?",
          type: 'regular' as const,
          created_at: minutesAgo(50),
          updated_at: minutesAgo(50),
          user: mockUser,
          html: "<p>I'm doing great, thanks! How about you?</p>",
          attachments: [],
          latest_reactions: [],
          own_reactions: [],
          reaction_counts: {},
          reaction_scores: {},
          reply_count: 0,
          status: 'received',
          cid: 'messaging:storybook-channel-1',
          mentioned_users: [],
        },
        {
          id: 'msg-3',
          text: 'Pretty good! Just working on some exciting stuff.',
          type: 'regular' as const,
          created_at: minutesAgo(30),
          updated_at: minutesAgo(30),
          user: participant,
          html: '<p>Pretty good! Just working on some exciting stuff.</p>',
          attachments: [],
          latest_reactions: [],
          own_reactions: [],
          reaction_counts: {},
          reaction_scores: {},
          reply_count: 0,
          status: 'received',
          cid: 'messaging:storybook-channel-1',
          mentioned_users: [],
        },
        ...(hasScrollableMessages
          ? Array.from({ length: SCROLLABLE_MESSAGE_COUNT }, (_, index) => {
              const isParticipantMessage = index % 2 === 0
              const user = isParticipantMessage ? participant : mockUser
              const text = isParticipantMessage
                ? `Here is another message to show how the redesigned header floats above the thread (${index + 1}).`
                : `Replying so the conversation has enough content to scroll under the header (${index + 1}).`

              return {
                id: `msg-extra-${index + 1}`,
                text,
                type: 'regular' as const,
                created_at: minutesAgo(Math.max(1, 25 - index)),
                updated_at: minutesAgo(Math.max(1, 25 - index)),
                user,
                html: `<p>${text}</p>`,
                attachments: [],
                latest_reactions: [],
                own_reactions: [],
                reaction_counts: {},
                reaction_scores: {},
                reply_count: 0,
                status: 'received',
                cid: 'messaging:storybook-channel-1',
                mentioned_users: [],
              }
            })
          : []),
      ]
    : []

  // Prepare channel data with optional follower status
  const channelData: Record<string, unknown> = {
    members: [mockUser.id, participant.id],
    frozen: isFrozen,
    ...(channelCustomerTag ? { customer_tag: channelCustomerTag } : {}),
  }

  // Add follower status if provided
  if (typeof followerStatus === 'string') {
    channelData.followerStatus = followerStatus
  } else if (typeof followerStatus === 'boolean') {
    channelData.isFollower = followerStatus
  }

  // Create a real channel using the client
  const channel = client.channel(
    'messaging',
    'storybook-channel-1',
    channelData
  )

  // Mock the watch method to return mocked data
  channel.watch = async () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    channel.state.messages = mockMessages as unknown as any[]
    channel.state.members = {
      [mockUser.id]: {
        user: {
          ...mockUser,
          is_account: false,
        },
        user_id: mockUser.id,
        role: 'owner',
        is_account: false,
      },
      [participant.id]: {
        user: {
          ...participant,
          is_account: true,
        },
        user_id: participant.id,
        role: 'member',
        is_account: true,
        ...(channelCustomerTag ? { is_follower: true } : {}),
      },
    }
    return {
      channel: channelData,
      members: [],
      messages: mockMessages,
      watchers: [],
      pinned_messages: [],
      duration: '0ms',
    } as unknown as QueryChannelAPIResponse
  }

  // Initialize the channel
  try {
    await channel.watch()
  } catch {
    // Ignore errors - we're in mock mode
  }

  // Force set the channel data after watch
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ;(channel as any)._data = channelData

  return channel
}

type TemplateProps = ComponentProps & {
  hasMessages?: boolean
  followerStatus?: string | boolean
  isFrozen?: boolean
  typingUser?: {
    id: string
    name?: string
    image?: string
  }
  channelCustomerTag?: string
  hasScrollableMessages?: boolean
}

const Template: StoryFn<TemplateProps> = (args) => {
  const {
    hasMessages = true,
    followerStatus,
    isFrozen = false,
    channelCustomerTag,
    hasScrollableMessages,
    typingUser,
    ...channelViewProps
  } = args
  const [client] = React.useState(() => createMockStreamChatClient(mockUser))

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

  useEffect(() => {
    createMockChannel(
      client,
      hasMessages,
      followerStatus,
      isFrozen,
      channelCustomerTag,
      hasScrollableMessages
    ).then((mockChannel) => {
      setChannel(mockChannel)
    })
  }, [
    client,
    hasMessages,
    followerStatus,
    isFrozen,
    channelCustomerTag,
    hasScrollableMessages,
  ])

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

    const timer = setTimeout(() => {
      client.dispatchEvent({
        type: 'typing.start',
        cid: channel.cid,
        user: typingUser,
      })
    }, 0)

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

  if (!channel) {
    return <div>Loading...</div>
  }

  return (
    <Chat client={client}>
      <div className="h-screen w-full bg-[#FBFAF9]">
        <ChannelView {...channelViewProps} channel={channel} />
      </div>
    </Chat>
  )
}

export const Default: StoryFn<TemplateProps> = Template.bind({})
Default.args = {
  showBackButton: false,
  onBack: () => console.log('Back clicked'),
  onLeaveConversation: (channel) =>
    console.log('Leave conversation:', channel.id),
  onBlockParticipant: (participantId) =>
    console.log('Block participant:', participantId),
  followerStatus: true,
}
Default.parameters = {
  docs: {
    description: {
      story: 'Default channel view with messages and conversation header.',
    },
  },
}

export const FloatingComposer: StoryFn<TemplateProps> = Template.bind({})
FloatingComposer.args = {
  ...Default.args,
  composerInput: FloatingComposerInput,
  hasScrollableMessages: true,
}
FloatingComposer.parameters = {
  docs: {
    description: {
      story:
        'Floating composer variant with the gradient and progressive blur chrome enabled by the custom composerInput prop.',
    },
  },
}

export const FloatingComposerWithConversationFooter: StoryFn<TemplateProps> =
  Template.bind({})
FloatingComposerWithConversationFooter.args = {
  ...FloatingComposer.args,
  renderConversationFooter: (channel) => (
    <div className="mx-4 mb-2 rounded-xl border border-black/10 bg-[#F9F7F4] px-4 py-3">
      <p className="text-sm text-charcoal">
        You will get notified when a new reply arrives in this conversation.
      </p>
      <button
        type="button"
        className="mt-2 rounded-full bg-charcoal px-3 py-1.5 text-xs font-medium text-white"
        onClick={() => console.log('Footer CTA clicked', channel.id)}
      >
        Open Notification Settings
      </button>
    </div>
  ),
}
FloatingComposerWithConversationFooter.parameters = {
  docs: {
    description: {
      story:
        'Floating composer with renderConversationFooter content kept inside the absolute chrome so footer controls remain clickable.',
    },
  },
}

export const ParticipantIdentityTrigger: StoryFn<TemplateProps> = Template.bind(
  {}
)
ParticipantIdentityTrigger.args = {
  showBackButton: false,
  showStarButton: true,
  channelCustomerTag: 'CUSTOMER_PAID',
  hasScrollableMessages: true,
  onParticipantNameClick: () =>
    window.alert('Participant details clicked — open Thread content'),
}
ParticipantIdentityTrigger.parameters = {
  docs: {
    description: {
      story:
        'Header with `onParticipantNameClick` set: the redesigned floating header centers the avatar and name pill, shows paid/star badges when present, and fires the callback when clicked (shows an alert here). With the prop omitted (see Default) the previous header remains in use.',
    },
  },
}

export const WithHeaderTitleBadges: StoryFn<TemplateProps> = Template.bind({})
WithHeaderTitleBadges.args = {
  showBackButton: false,
  renderHeaderTitleBadges: ({ participant }) => (
    <span
      aria-label={`${participant?.user?.name ?? 'Participant'} is a customer`}
      className="flex size-4 items-center justify-center rounded-full bg-[#E8F8EF] text-[10px] font-semibold text-[#136C3A]"
    >
      $
    </span>
  ),
}
WithHeaderTitleBadges.parameters = {
  docs: {
    description: {
      story:
        'Renders custom badges inline after the participant name in the channel header via renderHeaderTitleBadges.',
    },
  },
}

export const RestrictedOfficialChannel: StoryFn<TemplateProps> = Template.bind(
  {}
)
RestrictedOfficialChannel.args = {
  showBackButton: false,
  // Restricted surface for the Linktree official channel:
  showBlockParticipant: false,
  showReportParticipant: false,
  composerDisabled: true,
  composerDisabledReason: 'Only Linktree can send messages on this thread',
  onLeaveConversation: (channel) =>
    console.log('Leave conversation:', channel.id),
}
RestrictedOfficialChannel.parameters = {
  docs: {
    description: {
      story:
        'Restricted action surface used by the Linktree official channel: block and report are hidden, and the composer is replaced by a locked panel explaining the linker cannot send messages on this thread. Delete conversation and favorite remain available in the "..." actions popover.',
    },
  },
}

export const WithBackButton: StoryFn<TemplateProps> = Template.bind({})
WithBackButton.args = {
  showBackButton: true,
  onBack: () => console.log('Back clicked'),
  onLeaveConversation: (channel) =>
    console.log('Leave conversation:', channel.id),
  onBlockParticipant: (participantId) =>
    console.log('Block participant:', participantId),
}
WithBackButton.parameters = {
  viewport: {
    defaultViewport: 'mobile1',
  },
  docs: {
    description: {
      story:
        'Channel view with back button visible on mobile (switch to mobile viewport to see the back button).',
    },
  },
}

export const WithMessageActions: StoryFn<TemplateProps> = Template.bind({})
WithMessageActions.args = {
  showBackButton: true,
  onBack: () => console.log('Back clicked'),
  renderMessageInputActions: (channel) => (
    <button
      onClick={() => console.log('Custom action clicked', channel.id)}
      className="p-2 hover:bg-sand rounded-lg"
      aria-label="Attach file"
    >
      📎
    </button>
  ),
}
WithMessageActions.parameters = {
  viewport: {
    defaultViewport: 'mobile1',
  },
  docs: {
    description: {
      story:
        'Channel view with custom action buttons in the message input area (e.g., attachment button).',
    },
  },
}

export const WithConversationFooter: StoryFn<TemplateProps> = Template.bind({})
WithConversationFooter.args = {
  showBackButton: false,
  renderConversationFooter: (channel) => (
    <div className="mx-4 mb-2 rounded-xl border border-black/10 bg-[#F9F7F4] px-4 py-3">
      <p className="text-sm text-charcoal">
        You will get notified when a new reply arrives in this conversation.
      </p>
      <button
        type="button"
        className="mt-2 rounded-full bg-charcoal px-3 py-1.5 text-xs font-medium text-white"
        onClick={() => console.log('Footer CTA clicked', channel.id)}
      >
        Open Notification Settings
      </button>
    </div>
  ),
}
WithConversationFooter.parameters = {
  docs: {
    description: {
      story:
        'Channel view with a custom footer rendered between the message list and message input via renderConversationFooter(channel).',
    },
  },
}

export const WithMessageDecoration: StoryFn<TemplateProps> = Template.bind({})
WithMessageDecoration.args = {
  showBackButton: false,
  renderMessage: (messageNode, message) => {
    const isPriorityMessage = message.id === 'msg-3'

    if (!isPriorityMessage) {
      return messageNode
    }

    return (
      <section className="bg-[#F6F2FF] px-4 py-3 my-4 [&_.str-chat\_\_message-bubble]:bg-white">
        <header className="mb-3 flex items-center justify-between gap-3">
          <span className="text-sm font-semibold text-[#6D28D9]">
            ✧ Marked as a priority by AI
          </span>
          <span className="flex items-center gap-3">
            <button
              className="text-sm text-[#5B5662] underline underline-offset-2 hover:text-[#2E2A34]"
              type="button"
            >
              Not a priority
            </button>
            <button
              aria-label="Dismiss priority banner"
              className="text-base text-[#5B5662] hover:text-[#2E2A34]"
              type="button"
            >
              ×
            </button>
          </span>
        </header>
        {messageNode}
      </section>
    )
  },
}
WithMessageDecoration.parameters = {
  docs: {
    description: {
      story:
        'Decorates one message with a priority treatment using ChannelView renderMessage(messageNode, message).',
    },
  },
}

const WithStarButtonTemplate: StoryFn<ComponentProps> = (args) => {
  const [client] = React.useState(() => createMockStreamChatClient(mockUser))

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

  useEffect(() => {
    createMockChannel(client, true).then((mockChannel) => {
      const updateChannelPin = (isStarred: boolean) => {
        const member: ChannelMemberResponse = {
          user: mockUser,
          user_id: mockUser.id,
          pinned_at: isStarred ? now().toISOString() : null,
        }

        client.dispatchEvent({
          type: 'member.updated',
          cid: mockChannel.cid,
          member,
          user: mockUser,
        } as Event)
      }

      mockChannel.pin = async () => {
        updateChannelPin(true)
        return {} as QueryChannelAPIResponse
      }

      mockChannel.unpin = async () => {
        updateChannelPin(false)
        return {} as QueryChannelAPIResponse
      }

      setChannel(mockChannel)
    })
  }, [client])

  if (!channel) {
    return <div>Loading...</div>
  }

  return (
    <Chat client={client}>
      <div className="h-screen w-full bg-[#FBFAF9]">
        <ChannelView {...args} channel={channel} />
      </div>
    </Chat>
  )
}

export const WithStarButton: StoryFn<ComponentProps> =
  WithStarButtonTemplate.bind({})
WithStarButton.args = {
  showBackButton: false,
  showStarButton: true,
  onLeaveConversation: (channel) =>
    console.log('Leave conversation:', channel.id),
  onBlockParticipant: (participantId) =>
    console.log('Block participant:', participantId),
}
WithStarButton.argTypes = {
  showStarButton: {
    control: false,
    table: {
      disable: true,
    },
  },
}
WithStarButton.parameters = {
  docs: {
    description: {
      story: 'Channel view with a star button that toggles pinned state.',
    },
  },
}

const EmptyTemplate: StoryFn<ComponentProps> = (args) => {
  const [client] = React.useState(() => createMockStreamChatClient(mockUser))

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

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

  if (!channel) {
    return <div>Loading...</div>
  }

  return (
    <Chat client={client}>
      <div className="h-screen w-full bg-[#FBFAF9]">
        <ChannelView {...args} channel={channel} />
      </div>
    </Chat>
  )
}

export const EmptyChannel: StoryFn<ComponentProps> = EmptyTemplate.bind({})
EmptyChannel.args = {
  showBackButton: true,
  CustomChannelEmptyState: ChannelEmptyState,
}
EmptyChannel.parameters = {
  viewport: {
    defaultViewport: 'mobile1',
  },
  docs: {
    description: {
      story:
        'Channel view with no messages showing a custom empty state component.',
    },
  },
}

export const FrozenChannel: StoryFn<TemplateProps> = Template.bind({})
FrozenChannel.args = {
  showBackButton: false,
  isFrozen: true,
  renderMessageInputActions: (channel) => (
    <button
      onClick={() => console.log('Custom action clicked', channel.id)}
      className="p-2 hover:bg-sand rounded-lg"
      aria-label="Attach file"
      type="button"
    >
      📎
    </button>
  ),
}
FrozenChannel.parameters = {
  docs: {
    description: {
      story:
        'Channel view for a frozen conversation. The message composer renders in its disabled frozen state while the rest of the conversation remains readable.',
    },
  },
}

export const WithTypingIndicator: StoryFn<TemplateProps> = Template.bind({})
WithTypingIndicator.args = {
  showBackButton: false,
  typingUser: {
    id: mockParticipants[0].id,
    name: mockParticipants[0].name,
    image: mockParticipants[0].image,
  },
}
WithTypingIndicator.parameters = {
  // The typing indicator is animation-dominant, so a snapshot mostly captures
  // whichever frame the animation happened to pause at — a false-positive
  // source. Skip it in Chromatic; the story stays browsable in Storybook.
  chromatic: { disableSnapshot: true },
  docs: {
    description: {
      story:
        'Channel view with the typing indicator visible, driven by a mocked typing.start event from the other participant.',
    },
  },
}

// ---------------------------------------------------------------------------
// Custom SendButton stories
// ---------------------------------------------------------------------------

/** Demo send button: shows a camera icon when media is ready, arrow otherwise. */
const MediaAwareSendButton: React.FC<{
  sendMessage: (e?: React.BaseSyntheticEvent) => void
  disabled?: boolean
  hasMedia?: boolean
  [key: string]: unknown
}> = ({ sendMessage, disabled, hasMedia = false, ...rest }) => (
  <button
    {...rest}
    type="button"
    aria-label="Send"
    disabled={disabled}
    onClick={() => sendMessage()}
    className="str-chat__send-button mt-auto flex justify-center items-center flex-shrink-0 rounded-full size-8 bg-[#6D28D9] disabled:bg-[#F1F0EE] disabled:text-black/20 text-white"
    style={{ fontSize: 16 }}
  >
    {hasMedia ? '📷' : '🚀'}
  </button>
)

export const WithCustomSendButton: StoryFn<TemplateProps> = Template.bind({})
WithCustomSendButton.args = {
  showBackButton: false,
  sendButton: MediaAwareSendButton,
}
WithCustomSendButton.parameters = {
  docs: {
    description: {
      story:
        'Passes a custom `sendButton` component via the `sendButton` prop. Stream `Channel` receives it as `SendButton` so it overrides the default arrow button without breaking `WithComponents` overrides. The purple rocket button here is a placeholder; real-world usage is a media-aware button that injects a `LocalAttachment` before sending.',
    },
  },
}
