import {EyeSlashIcon, FlagIcon} from '@heroicons/react/24/outline'
import {ArrowUturnLeftIcon, EllipsisHorizontalIcon} from '@heroicons/react/24/solid'
import {Editor} from '@tiptap/react'
import clsx from 'clsx'
import {type Comment, MAX_COMMENT_LENGTH, ReplyToUserInfo} from 'common/comment'
import {buildArray} from 'common/util/array'
import {ChevronDown, ChevronUp} from 'lucide-react'
import {memo, ReactNode, useEffect, useRef, useState} from 'react'
import {toast} from 'react-hot-toast'
import {Button, IconButton} from 'web/components/buttons/button'
import {ReportModal} from 'web/components/buttons/report-button'
import {CommentInputTextArea} from 'web/components/comments/comment-input'
import DropdownMenu from 'web/components/comments/dropdown-menu'
import {ReplyToggle} from 'web/components/comments/reply-toggle'
import {Col} from 'web/components/layout/col'
import {Row} from 'web/components/layout/row'
import {RelativeTimestamp} from 'web/components/relative-timestamp'
import {Avatar} from 'web/components/widgets/avatar'
import {Content, useTextEditor} from 'web/components/widgets/editor'
import {Tooltip} from 'web/components/widgets/tooltip'
import {UserLink} from 'web/components/widgets/user-link'
import {useAdmin} from 'web/hooks/use-admin'
import {useEvent} from 'web/hooks/use-event'
import {useProfileByUserId} from 'web/hooks/use-profile'
import {useUser} from 'web/hooks/use-user'
import {api} from 'web/lib/api'
import {firebaseLogin, User} from 'web/lib/firebase/users'
import {useT} from 'web/lib/locale'
import {track} from 'web/lib/service/analytics'
import {safeLocalStorage} from 'web/lib/util/local'
import {scrollIntoViewCentered} from 'web/lib/util/scroll'

export function ProfileProfileCommentThread(props: {
  onUser: User
  threadComments: Comment[]
  parentComment: Comment
  trackingLocation: string
  collapseMiddle?: boolean
  inTimeline?: boolean
  idInUrl?: string
  showReplies?: boolean
  className?: string
}) {
  const {
    onUser,
    threadComments,
    parentComment,
    collapseMiddle,
    trackingLocation,
    idInUrl,
    showReplies,
    className,
  } = props
  const [replyToUserInfo, setReplyToUserInfo] = useState<ReplyToUserInfo>()

  const idInThisThread = idInUrl && threadComments.map((comment) => comment.id).includes(idInUrl)

  const [seeReplies, setSeeReplies] = useState(
    !parentComment.hidden && (showReplies || !!idInThisThread),
  )

  const onSeeRepliesClick = useEvent(() => setSeeReplies(!seeReplies))
  const clearReply = useEvent(() => setReplyToUserInfo(undefined))
  const onReplyClick = useEvent((comment: Comment) => {
    setSeeReplies(true)
    setReplyToUserInfo({id: comment.id, username: comment.userUsername})
  })
  const [collapseToIndex, setCollapseToIndex] = useState<number>(
    collapseMiddle && threadComments.length > 2 ? threadComments.length - 2 : Infinity,
  )
  return (
    <Col className={clsx('mt-3 items-stretch gap-3', className)}>
      <ParentProfileComment
        onUser={onUser}
        key={parentComment.id}
        comment={parentComment}
        highlighted={idInUrl === parentComment.id}
        seeReplies={seeReplies}
        numReplies={threadComments.length}
        onSeeReplyClick={onSeeRepliesClick}
        onReplyClick={onReplyClick}
        trackingLocation={trackingLocation}
      />
      {seeReplies &&
        threadComments
          .slice(0, collapseToIndex)
          .map((comment) => (
            <ProfileComment
              onUser={onUser}
              key={comment.id}
              comment={comment}
              highlighted={idInUrl === comment.id}
              onReplyClick={onReplyClick}
              trackingLocation={trackingLocation}
            />
          ))}
      {seeReplies && threadComments.length > collapseToIndex && (
        <Row
          className={'justify-end sm:-mb-2 sm:mt-1'}
          key={parentComment.id + 'see-replies-feed-button'}
        >
          <Button
            size={'xs'}
            color={'gray-white'}
            onClick={() => {
              setCollapseToIndex(Infinity)
            }}
          >
            <Col>
              <ChevronUp className={'mr-2 h-2'} />
              <ChevronDown className={'mr-2 h-2'} />
            </Col>
            See {threadComments.length - 1} replies
          </Button>
        </Row>
      )}
      {replyToUserInfo && (
        <div className="stop-prop flex">
          <div className="border-ink-100 -mt-3 ml-4 h-7 w-4 rounded-bl-xl border-b-2 border-l-2" />
          <ProfileCommentInput
            onUserId={onUser.id}
            parentCommentId={parentComment.id}
            replyToUserInfo={replyToUserInfo}
            clearReply={clearReply}
            trackingLocation={trackingLocation}
            className="w-full min-w-0 grow"
          />
        </div>
      )}
    </Col>
  )
}

const ProfileComment = memo(function FeedComment(props: {
  onUser: User
  comment: Comment
  trackingLocation: string
  highlighted?: boolean
  onReplyClick?: (comment: Comment) => void
  children?: ReactNode
  isParent?: boolean
}) {
  const {onUser, highlighted, onReplyClick, children, trackingLocation, isParent} = props
  const ref = useRef<HTMLDivElement>(null)
  const [comment, setComment] = useState(props.comment)
  const {userUsername, userAvatarUrl, userId, hidden} = comment
  const isOwner = onUser.id === userId
  const profile = useProfileByUserId(userId)

  useEffect(() => {
    if (highlighted && ref.current) {
      scrollIntoViewCentered(ref.current)
    }
  }, [highlighted])

  return (
    <Col className="group">
      <Row ref={ref} className={clsx(isParent ? 'gap-2' : 'gap-1')}>
        <Row className="relative">
          {!isParent && (
            <div className="border-ink-100 dark:border-ink-300 -mt-4 ml-4 h-6 w-4 rounded-bl-xl border-b-2 border-l-2" />
          )}
          <Avatar
            username={userUsername}
            size={isParent ? 'sm' : '2xs'}
            avatarUrl={profile?.pinned_url ?? userAvatarUrl}
            className={clsx(isOwner && 'shadow shadow-amber-300', 'z-10')}
          />
          <div
            className={clsx(
              'bg-ink-100 dark:bg-ink-300 absolute bottom-0 left-4 w-0.5 group-last:hidden ',
              isParent ? 'top-0' : '-top-1',
            )}
          />
        </Row>

        <Col
          className={clsx(
            'grow rounded-lg rounded-tl-none px-3 pb-0.5 pt-1 transition-colors',
            highlighted
              ? 'bg-primary-100 border-primary-300 border-2'
              : 'bg-canvas-50 drop-shadow-sm',
          )}
        >
          <FeedCommentHeader
            comment={comment}
            onUser={onUser}
            isParent={isParent}
            onHide={() => setComment({...comment, hidden: !comment.hidden})}
          />

          {hidden ? (
            <span className={'text-ink-500 text-sm italic'}>Comment deleted</span>
          ) : (
            <Content size="sm" className="mt-1 grow" content={comment.content} />
          )}

          <Row>
            {children}
            <CommentActions
              onReplyClick={onReplyClick}
              comment={comment}
              trackingLocation={trackingLocation}
            />
          </Row>
        </Col>
      </Row>
    </Col>
  )
})

const ParentProfileComment = memo(function ParentFeedComment(props: {
  onUser: User
  comment: Comment
  highlighted?: boolean
  seeReplies: boolean
  numReplies: number
  onReplyClick?: (comment: Comment) => void
  onSeeReplyClick: () => void
  trackingLocation: string
}) {
  const {
    onUser,
    comment,
    highlighted,
    onReplyClick,
    onSeeReplyClick,
    seeReplies,
    numReplies,
    trackingLocation,
  } = props

  return (
    <ProfileComment
      onUser={onUser}
      comment={comment}
      onReplyClick={onReplyClick}
      highlighted={highlighted}
      trackingLocation={trackingLocation}
      isParent={true}
    >
      <ReplyToggle
        seeReplies={seeReplies}
        numComments={numReplies}
        onSeeReplyClick={onSeeReplyClick}
      />
    </ProfileComment>
  )
})

function DotMenu(props: {onUser: User; comment: Comment; onHide: () => void}) {
  const {comment, onHide, onUser} = props
  const [isModalOpen, setIsModalOpen] = useState(false)
  const user = useUser()
  const isCurrentUser = user?.id === comment.userId
  const isOwner = onUser.id === user?.id
  const isAdmin = useAdmin()

  return (
    <>
      <ReportModal
        report={{
          contentOwnerId: comment.userId,
          contentId: comment.id,
          contentType: 'comment',
          parentId: onUser.id,
          parentType: 'user',
        }}
        setIsModalOpen={setIsModalOpen}
        isModalOpen={isModalOpen}
        label={'Comment'}
      />
      <DropdownMenu
        menuWidth={'w-36'}
        closeOnClick={true}
        icon={<EllipsisHorizontalIcon className="mt-[0.12rem] h-4 w-4" aria-hidden="true" />}
        items={buildArray(
          user &&
            comment.userId !== user.id && {
              name: 'Report',
              icon: <FlagIcon className="h-5 w-5" />,
              onClick: () => {
                if (user?.id !== comment.userId) setIsModalOpen(true)
                else toast.error(`You can't report your own comment`)
              },
            },
          (isAdmin || isCurrentUser || isOwner) && {
            name: comment.hidden ? 'Undelete' : 'Delete',
            icon: <EyeSlashIcon className="h-5 w-5 text-red-500" />,
            onClick: async () => {
              onHide()
              await toast.promise(
                api('hide-comment', {
                  commentId: comment.id,
                  hide: !comment.hidden,
                }),
                {
                  loading: comment.hidden ? 'Undeleting comment...' : 'Deleting comment...',
                  success: () => {
                    return comment.hidden ? 'Comment undeleted' : 'Comment deleted'
                  },
                  error: () => {
                    return comment.hidden ? 'Error undeleting comment' : 'Error deleting comment'
                  },
                },
              )
            },
          },
        )}
      />
    </>
  )
}

function CommentActions(props: {
  onReplyClick?: (comment: Comment) => void
  comment: Comment
  trackingLocation: string
}) {
  const {onReplyClick, comment} = props
  const user = useUser()

  return (
    <Row className="grow items-center justify-end">
      {user && onReplyClick && (
        <Tooltip text="Reply" placement="bottom">
          <IconButton
            size={'xs'}
            onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
              e.preventDefault()
              e.stopPropagation()
              onReplyClick(comment)
            }}
            className={'text-ink-500'}
          >
            <ArrowUturnLeftIcon className="h-5 w-5 " />
          </IconButton>
        </Tooltip>
      )}
    </Row>
  )
}

export function ProfileCommentInput(props: {
  onUserId: string
  className?: string
  replyToUserInfo?: ReplyToUserInfo
  parentCommentId?: string
  clearReply?: () => void
  trackingLocation: string
}) {
  const {parentCommentId, onUserId, replyToUserInfo, className, clearReply, trackingLocation} =
    props
  const t = useT()
  const user = useUser()
  const onSubmitComment = useEvent(async (editor: Editor) => {
    if (!user) {
      track('sign in to comment')
      await firebaseLogin()
      return
    }
    await api('create-comment', {
      userId: onUserId,
      content: editor.getJSON(),
      replyToCommentId: parentCommentId,
    })
    clearReply?.()
    track('comment', {
      location: trackingLocation,
    })
  })
  return (
    <CommentInput
      pageId={trackingLocation}
      onSubmitComment={onSubmitComment}
      replyToUserInfo={replyToUserInfo}
      parentCommentId={parentCommentId}
      placeholder={t('profile.comments.placeholder', 'Write your endorsement...')}
      className={className}
    />
  )
}

function CommentInput(props: {
  replyToUserInfo?: ReplyToUserInfo
  // Reply to another comment
  parentCommentId?: string
  onSubmitComment: (editor: Editor) => Promise<void>
  // unique id for autosave
  pageId: string
  className?: string
  blocked?: boolean
  placeholder?: string
}) {
  const {
    parentCommentId,
    replyToUserInfo,
    onSubmitComment,
    pageId,
    className,
    blocked,
    placeholder = 'Write a comment...',
  } = props
  const user = useUser()

  const key = `comment ${pageId} ${parentCommentId ?? ''}`

  const editor = useTextEditor({
    key,
    size: 'sm',
    max: MAX_COMMENT_LENGTH,
    placeholder,
  })

  const [isSubmitting, setIsSubmitting] = useState(false)

  async function submitComment() {
    if (!editor || editor.isEmpty || isSubmitting) return
    setIsSubmitting(true)
    editor.commands.focus('end')
    // if last item is text, try to linkify it by adding and deleting a space
    if (editor.state.selection.empty) {
      editor.commands.insertContent(' ')
      const endPos = editor.state.selection.from
      editor.commands.deleteRange({from: endPos - 1, to: endPos})
    }

    try {
      await onSubmitComment?.(editor)
      editor.commands.clearContent(true)
      // force clear save, because it can fail if editor unrenders
      safeLocalStorage?.removeItem(`text ${key}`)
    } catch (e) {
      console.error(e)
      toast.error('Error submitting. Try again?')
    } finally {
      setIsSubmitting(false)
    }
  }

  if (user?.isBannedFromPosting) return <></>

  return blocked ? (
    <div className={'text-ink-500 mb-3 text-sm'}>
      You blocked the creator or they blocked you, so you can't comment.
    </div>
  ) : (
    <Row className={clsx(className, 'mb-2 w-full gap-1 sm:gap-2')}>
      <Avatar avatarUrl={user?.avatarUrl} username={user?.username} size="sm" />
      <CommentInputTextArea
        editor={editor}
        replyTo={replyToUserInfo}
        user={user}
        submit={submitComment}
        isSubmitting={isSubmitting}
      />
    </Row>
  )
}

function FeedCommentHeader(props: {
  onUser: User
  comment: Comment
  onHide: () => void
  isParent?: boolean
}) {
  const {comment, onUser, onHide} = props
  const {userUsername, userName, userId} = comment

  return (
    <Col className={clsx('text-ink-600 text-sm ')}>
      <Row className="justify-between">
        <Row className=" gap-1">
          <span>
            <UserLink
              user={{id: userId, username: userUsername, name: userName}}
              className={'font-semibold'}
            />
          </span>
          <RelativeTimestamp shortened={true} time={comment.createdTime} />
          <DotMenu onHide={onHide} comment={comment} onUser={onUser} />
        </Row>
      </Row>
    </Col>
  )
}
