import { Row } from 'web/components/layout/row'
import { Col } from 'web/components/layout/col'
import { PrivateUser, User } from 'common/user'
import React, { useEffect, memo, useState, useMemo } from 'react'
import { Avatar } from 'web/components/avatar'
import { Group } from 'common/group'
import { Comment, GroupComment } from 'common/comment'
import { createCommentOnGroup } from 'web/lib/firebase/comments'
import { CommentInputTextArea } from 'web/components/feed/feed-comments'
import { track } from 'web/lib/service/analytics'
import { firebaseLogin } from 'web/lib/firebase/users'
import { useRouter } from 'next/router'
import clsx from 'clsx'
import { UserLink } from 'web/components/user-page'
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
import { CommentTipMap, CommentTips } from 'web/hooks/use-tip-txns'
import { Tipper } from 'web/components/tipper'
import { sum } from 'lodash'
import { formatMoney } from 'common/util/format'
import { useWindowSize } from 'web/hooks/use-window-size'
import { Content, useTextEditor } from 'web/components/editor'
import { useUnseenPreferredNotifications } from 'web/hooks/use-notifications'
import { ChevronDownIcon, UsersIcon } from '@heroicons/react/outline'
import { setNotificationsAsSeen } from 'web/pages/notifications'
import { usePrivateUser } from 'web/hooks/use-user'

export function GroupChat(props: {
  messages: GroupComment[]
  user: User | null | undefined
  group: Group
  tips: CommentTipMap
}) {
  const { messages, user, group, tips } = props

  const privateUser = usePrivateUser()

  const { editor, upload } = useTextEditor({
    simple: true,
    placeholder: 'Send a message',
  })
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [scrollToBottomRef, setScrollToBottomRef] =
    useState<HTMLDivElement | null>(null)
  const [scrollToMessageId, setScrollToMessageId] = useState('')
  const [scrollToMessageRef, setScrollToMessageRef] =
    useState<HTMLDivElement | null>(null)
  const [replyToUser, setReplyToUser] = useState<any>()

  const router = useRouter()
  const isMember = user && group.memberIds.includes(user?.id)

  const { width, height } = useWindowSize()
  const [containerRef, setContainerRef] = useState<HTMLDivElement | null>(null)
  // Subtract bottom bar when it's showing (less than lg screen)
  const bottomBarHeight = (width ?? 0) < 1024 ? 58 : 0
  const remainingHeight =
    (height ?? 0) - (containerRef?.offsetTop ?? 0) - bottomBarHeight

  // array of groups, where each group is an array of messages that are displayed as one
  const groupedMessages = useMemo(() => {
    // Group messages with createdTime within 2 minutes of each other.
    const tempGrouped: GroupComment[][] = []
    for (let i = 0; i < messages.length; i++) {
      const message = messages[i]
      if (i === 0) tempGrouped.push([message])
      else {
        const prevMessage = messages[i - 1]
        const diff = message.createdTime - prevMessage.createdTime
        const creatorsMatch = message.userId === prevMessage.userId
        if (diff < 2 * 60 * 1000 && creatorsMatch) {
          tempGrouped.at(-1)?.push(message)
        } else {
          tempGrouped.push([message])
        }
      }
    }

    return tempGrouped
  }, [messages])

  useEffect(() => {
    scrollToMessageRef?.scrollIntoView()
  }, [scrollToMessageRef])

  useEffect(() => {
    if (scrollToBottomRef)
      scrollToBottomRef.scrollTo({ top: scrollToBottomRef.scrollHeight || 0 })
    // Must also listen to groupedMessages as they update the height of the messaging window
  }, [scrollToBottomRef, groupedMessages])

  useEffect(() => {
    const elementInUrl = router.asPath.split('#')[1]
    if (messages.map((m) => m.id).includes(elementInUrl)) {
      setScrollToMessageId(elementInUrl)
    }
  }, [messages, router.asPath])

  useEffect(() => {
    // is mobile?
    if (width && width > 720) focusInput()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [width])

  function onReplyClick(comment: Comment) {
    setReplyToUser({ id: comment.userId, username: comment.userUsername })
  }

  async function submitMessage() {
    if (!user) {
      track('sign in to comment')
      return await firebaseLogin()
    }
    if (!editor || editor.isEmpty || isSubmitting) return
    setIsSubmitting(true)
    await createCommentOnGroup(group.id, editor.getJSON(), user)
    editor.commands.clearContent()
    setIsSubmitting(false)
    setReplyToUser(undefined)
    focusInput()
  }
  function focusInput() {
    editor?.commands.focus()
  }

  return (
    <Col ref={setContainerRef} style={{ height: remainingHeight }}>
      <Col
        className={
          'w-full flex-1 space-y-2 overflow-x-hidden overflow-y-scroll pt-2'
        }
        ref={setScrollToBottomRef}
      >
        {groupedMessages.map((messages) => (
          <GroupMessage
            user={user}
            key={`group ${messages[0].id}`}
            comments={messages}
            group={group}
            onReplyClick={onReplyClick}
            highlight={messages[0].id === scrollToMessageId}
            setRef={
              scrollToMessageId === messages[0].id
                ? setScrollToMessageRef
                : undefined
            }
            tips={tips[messages[0].id] ?? {}}
          />
        ))}
        {messages.length === 0 && (
          <div className="p-2 text-gray-500">
            No messages yet. Why not{isMember ? ` ` : ' join and '}
            <button
              className={'cursor-pointer font-bold text-gray-700'}
              onClick={focusInput}
            >
              add one?
            </button>
          </div>
        )}
      </Col>
      {user && group.memberIds.includes(user.id) && (
        <div className="flex w-full justify-start gap-2 p-2">
          <div className="mt-1">
            <Avatar
              username={user?.username}
              avatarUrl={user?.avatarUrl}
              size={'sm'}
            />
          </div>
          <div className={'flex-1'}>
            <CommentInputTextArea
              editor={editor}
              upload={upload}
              user={user}
              replyToUser={replyToUser}
              submitComment={submitMessage}
              isSubmitting={isSubmitting}
              submitOnEnter
            />
          </div>
        </div>
      )}

      {privateUser && (
        <GroupChatNotificationsIcon
          group={group}
          privateUser={privateUser}
          shouldSetAsSeen={true}
          hidden={true}
        />
      )}
    </Col>
  )
}

export function GroupChatInBubble(props: {
  messages: GroupComment[]
  user: User | null | undefined
  privateUser: PrivateUser | null | undefined
  group: Group
  tips: CommentTipMap
}) {
  const { messages, user, group, tips, privateUser } = props
  const [shouldShowChat, setShouldShowChat] = useState(false)
  const router = useRouter()

  useEffect(() => {
    const groupsWithChatEmphasis = [
      'welcome',
      'bugs',
      'manifold-features-25bad7c7792e',
      'updates',
    ]
    if (
      router.asPath.includes('/chat') ||
      groupsWithChatEmphasis.includes(
        router.asPath.split('/group/')[1].split('/')[0]
      )
    ) {
      setShouldShowChat(true)
    }
    // Leave chat open between groups if user is using chat?
    else {
      setShouldShowChat(false)
    }
  }, [router.asPath])

  return (
    <Col
      className={clsx(
        'fixed right-0 bottom-[0px] h-1 w-full sm:bottom-[20px] sm:right-20 sm:w-2/3 md:w-1/2 lg:right-24 lg:w-1/3 xl:right-32 xl:w-1/4',
        shouldShowChat ? 'p-2m z-10 h-screen bg-white' : ''
      )}
    >
      {shouldShowChat && (
        <GroupChat messages={messages} user={user} group={group} tips={tips} />
      )}
      <button
        type="button"
        className={clsx(
          'fixed right-1 inline-flex items-center rounded-full border md:right-2 lg:right-5 xl:right-10' +
            ' border-transparent p-3 text-white shadow-sm lg:p-4' +
            ' focus:outline-none focus:ring-2  focus:ring-offset-2 ' +
            ' bottom-[70px] ',
          shouldShowChat
            ? 'bottom-auto top-2 bg-gray-600 hover:bg-gray-400 focus:ring-gray-500 sm:bottom-[70px] sm:top-auto '
            : ' bg-indigo-600  hover:bg-indigo-700 focus:ring-indigo-500'
        )}
        onClick={() => {
          // router.push('/chat')
          setShouldShowChat(!shouldShowChat)
          track('mobile group chat button')
        }}
      >
        {!shouldShowChat ? (
          <UsersIcon className="h-10 w-10" aria-hidden="true" />
        ) : (
          <ChevronDownIcon className={'h-10 w-10'} aria-hidden={'true'} />
        )}
        {privateUser && (
          <GroupChatNotificationsIcon
            group={group}
            privateUser={privateUser}
            shouldSetAsSeen={shouldShowChat}
            hidden={false}
          />
        )}
      </button>
    </Col>
  )
}

function GroupChatNotificationsIcon(props: {
  group: Group
  privateUser: PrivateUser
  shouldSetAsSeen: boolean
  hidden: boolean
}) {
  const { privateUser, group, shouldSetAsSeen, hidden } = props
  const preferredNotificationsForThisGroup = useUnseenPreferredNotifications(
    privateUser,
    {
      customHref: `/group/${group.slug}`,
    }
  )
  useEffect(() => {
    preferredNotificationsForThisGroup.forEach((notification) => {
      if (
        (shouldSetAsSeen && notification.isSeenOnHref?.includes('chat')) ||
        // old style chat notif that simply ended with the group slug
        notification.isSeenOnHref?.endsWith(group.slug)
      ) {
        setNotificationsAsSeen([notification])
      }
    })
  }, [group.slug, preferredNotificationsForThisGroup, shouldSetAsSeen])

  return (
    <div
      className={
        !hidden &&
        preferredNotificationsForThisGroup.length > 0 &&
        !shouldSetAsSeen
          ? 'absolute right-4 top-4 h-3 w-3 rounded-full border-2 border-white bg-red-500'
          : 'hidden'
      }
    ></div>
  )
}

const GroupMessage = memo(function GroupMessage_(props: {
  user: User | null | undefined
  comments: GroupComment[]
  group: Group
  onReplyClick?: (comment: Comment) => void
  setRef?: (ref: HTMLDivElement) => void
  highlight?: boolean
  tips: CommentTips
}) {
  const { comments, onReplyClick, group, setRef, highlight, user, tips } = props
  const first = comments[0]
  const { id, userUsername, userName, userAvatarUrl, createdTime } = first

  const isCreatorsComment = user && first.userId === user.id
  return (
    <Col
      ref={setRef}
      className={clsx(
        isCreatorsComment ? 'mr-2 self-end' : '',
        'w-fit max-w-sm gap-1 space-x-3 rounded-md bg-white p-1 text-sm text-gray-500 transition-colors duration-1000  sm:max-w-md sm:p-3 sm:leading-[1.3rem]',
        highlight ? `-m-1 bg-indigo-500/[0.2] p-2` : ''
      )}
    >
      <Row className={'items-center'}>
        {!isCreatorsComment && (
          <Col>
            <Avatar
              className={'mx-2 ml-2.5'}
              size={'xs'}
              username={userUsername}
              avatarUrl={userAvatarUrl}
            />
          </Col>
        )}
        {!isCreatorsComment ? (
          <UserLink username={userUsername} name={userName} />
        ) : (
          <span className={'ml-2.5'}>{'You'}</span>
        )}
        <CopyLinkDateTimeComponent
          prefix={'group'}
          slug={group.slug}
          createdTime={createdTime}
          elementId={id}
        />
      </Row>
      <div className="mt-2 text-base text-black">
        {comments.map((comment) => (
          <Content
            key={comment.id}
            content={comment.content || comment.text}
            smallImage
          />
        ))}
      </div>
      <Row>
        {!isCreatorsComment && onReplyClick && (
          <button
            className={
              'self-start py-1 text-xs font-bold text-gray-500 hover:underline'
            }
            onClick={() => onReplyClick(first)}
          >
            Reply
          </button>
        )}
        {isCreatorsComment && sum(Object.values(tips)) > 0 && (
          <span className={'text-primary'}>
            {formatMoney(sum(Object.values(tips)))}
          </span>
        )}
        {!isCreatorsComment && <Tipper comment={first} tips={tips} />}
      </Row>
    </Col>
  )
})