'use client';

import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import clsx from 'clsx';
import { useTranslation } from 'next-i18next';
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
import { twMerge } from 'tailwind-merge';
import { useResizeObserver } from 'usehooks-ts';

import { useStores } from '@/app/(root)/AppProviders';
import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import Avatar from '@/components/image/Avatar';
import ProfileLink from '@/components/link/ProfileLink';
import ListItem, {
  Props as ListItemProps,
} from '@/components/listView/ListItem';
import {
  ChevronDownIcon,
  ChevronUpIcon,
  EyeSlashIcon,
  FlagIcon,
  HeartIcon,
  HeartOutlineIcon,
  MoreHorizontalIcon,
  ProhibitionIcon,
  ShieldIcon,
  TrashIcon,
} from '@/icons';
import logWebUserEvent from '@/logging/logWebUserEvent';
import { CommentEntityType } from '@/state/clipStore';
import { formatRelativeTime } from '@/utils/time';
import { decodeTimeFormat, encodeTimeFormat } from '@/utils/utils';

import {
  type CommentContentAnnotation,
  type CommentProps,
  formatCommentText,
} from './utils';

type CommentActionPayload<P extends object = object> = {
  index?: number;
  id: string;
  type: CommentEntityType;
} & P;

type CommentActionProps = Pick<
  CommentProps,
  | 'commentId'
  | 'entityType'
  | 'numLikes'
  | 'isLiked'
  | 'handle'
  | 'userCommentsBlocked'
> & {
  onLike?: (
    payload: CommentActionPayload<{ like?: boolean }>,
    e?: Event | React.MouseEvent
  ) => void;
  onDelete?: (
    payload: CommentActionPayload,
    e?: Event | React.MouseEvent
  ) => void;
  onModeratorDelete?: (
    payload: CommentActionPayload,
    e?: Event | React.MouseEvent
  ) => void;
  onHide?: (
    payload: CommentActionPayload,
    e?: Event | React.MouseEvent
  ) => void;
  onReport?: (
    payload: CommentActionPayload<{ reason?: string }>,
    e?: Event | React.MouseEvent
  ) => void;
  onBlock?: (
    payload: CommentActionPayload<{ handle: string; reason?: string }>,
    e?: Event | React.MouseEvent
  ) => void;
  onUnblock?: (
    payload: CommentActionPayload<{ handle: string }>,
    e?: Event | React.MouseEvent
  ) => void;
  disablePortal?: boolean;
};

export type CommentListItemProps = Omit<ListItemProps, 'start' | 'end'> &
  CommentProps &
  CommentActionProps & {
    contentClassName?: string;
    paragraphClassName?: string;
    readonly?: boolean;
    contentLineClamp?: number;
    onProfileClick?: (
      payload: CommentActionPayload<{
        clipId?: string;
        commentType?: 'comment' | 'reply';
        handle: string;
        context?: 'handle' | 'avatar';
      }>,
      e?: React.MouseEvent
    ) => void;
    onReplyClick?: (e?: React.MouseEvent) => void;
    onView?: (payload: CommentActionPayload) => void;
    currentTime?: string;
    isCreator?: boolean;
    isOriginalCreator?: boolean;
    disablePortal?: boolean;
    parentEntityType?: CommentEntityType; // New prop for parent context
  };

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof CommentListItemProps
> &
  CommentListItemProps;

const CommentActions: React.FC<
  Omit<React.HTMLAttributes<HTMLDivElement>, keyof CommentActionProps> &
    CommentActionProps
> = (props) => {
  const {
    className,
    handle,
    commentId,
    entityType,
    isLiked,
    numLikes,
    onLike,
    onDelete,
    onModeratorDelete,
    onHide,
    onReport,
    onBlock,
    onUnblock,
    disablePortal = false,
  } = props;

  const { t } = useTranslation();

  const dropdownContent = (
    <DropdownMenu.Content
      align='end'
      className={clsx(
        'flex flex-col items-start justify-start gap-1 rounded-md border',
        'z-10 min-w-[160px] overflow-clip py-1',
        'md:box-shadow border-border-primary bg-background-secondary text-foreground-secondary',
        'font-sans text-sm font-medium'
      )}
    >
      {onHide && (
        <DropdownMenu.Item
          onSelect={(e) => onHide({ id: commentId, type: entityType }, e)}
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start'
            icon={EyeSlashIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('comments.hide')}
          </Button>
        </DropdownMenu.Item>
      )}
      {onDelete && (
        <DropdownMenu.Item
          onSelect={(e) => onDelete({ id: commentId, type: entityType }, e)}
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start'
            icon={TrashIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('actions.delete')}
          </Button>
        </DropdownMenu.Item>
      )}
      {onModeratorDelete && !onDelete && (
        <DropdownMenu.Item
          onSelect={(e) =>
            onModeratorDelete({ id: commentId, type: entityType }, e)
          }
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start text-accent-error-on-primary'
            icon={ShieldIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('actions.delete')}
          </Button>
        </DropdownMenu.Item>
      )}
      {onReport && (
        <DropdownMenu.Item
          onSelect={(e) => onReport({ id: commentId, type: entityType }, e)}
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start'
            icon={FlagIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('actions.report')}
          </Button>
        </DropdownMenu.Item>
      )}
      {onBlock && !!handle && (
        <DropdownMenu.Item
          onSelect={(e) =>
            onBlock({ id: commentId, type: entityType, handle }, e)
          }
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start'
            icon={ProhibitionIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('actions.block')}
          </Button>
        </DropdownMenu.Item>
      )}
      {onUnblock && !!handle && (
        <DropdownMenu.Item
          onSelect={(e) =>
            onUnblock({ id: commentId, type: entityType, handle }, e)
          }
          asChild
        >
          <Button
            className='w-full'
            contentClassName='justify-start'
            icon={ProhibitionIcon}
            variant={ButtonVariant.Tertiary}
            shape={ButtonShape.Rectangle}
            size={ButtonSize.Mini}
          >
            {t('actions.unblock')}
          </Button>
        </DropdownMenu.Item>
      )}
    </DropdownMenu.Content>
  );

  const renderedDropdownContent = disablePortal ? (
    dropdownContent
  ) : (
    <DropdownMenu.Portal>{dropdownContent}</DropdownMenu.Portal>
  );

  const hasMoreActions = !!(
    onDelete ||
    onModeratorDelete ||
    onHide ||
    onReport ||
    onBlock ||
    onUnblock
  );

  return (
    <div className={twMerge('flex flex-row gap-1', className)}>
      <div className='relative'>
        <Button
          icon={isLiked ? HeartIcon : HeartOutlineIcon}
          iconClassName={clsx('w-5 h-5 m-0.5', {
            'text-accent-pink-on-primary': isLiked,
          })}
          aspectSquare
          aria-label={t('actions.moreActions')}
          variant={ButtonVariant.Tertiary}
          active={false}
          shape={ButtonShape.Rounded}
          size={ButtonSize.Mini}
          href={undefined}
          onClick={
            onLike &&
            ((e) =>
              onLike({ id: commentId, type: entityType, like: !isLiked }, e))
          }
        >
          {numLikes ? (
            <span
              className={clsx(
                'absolute inset-x-0 top-full text-foreground-secondary',
                'text-center font-mono text-[10px] leading-snug font-semibold'
              )}
            >
              {numLikes}
            </span>
          ) : null}
        </Button>
      </div>
      {hasMoreActions && (
        <div>
          <DropdownMenu.Root>
            <DropdownMenu.Trigger asChild>
              <Button
                className='cursor-pointer'
                enableHoverState
                icon={MoreHorizontalIcon}
                aria-label={t('actions.moreActions')}
                variant={ButtonVariant.Tertiary}
                active={false}
                shape={ButtonShape.Rounded}
                size={ButtonSize.Mini}
              />
            </DropdownMenu.Trigger>
            {renderedDropdownContent}
          </DropdownMenu.Root>
        </div>
      )}
    </div>
  );
};

const CommentListItem: React.FC<Props> = (props) => {
  const {
    children,
    className,
    readonly,
    contentLineClamp = 10,
    contentClassName = 'line-clamp-(--comment-line-clamp,none)',
    paragraphClassName = 'first:mt-0 mt-1',
    currentTime,
    clipId,
    commentId,
    hookId,
    userId,
    entityType,
    parentId,
    avatarUrl,
    handle,
    userCommentsBlocked,
    displayName,
    profileHref = `/@${handle}`,
    numLikes,
    isLiked,
    isBlocked,
    createdAt,
    content,
    userMentions,
    trackTimestamp,
    onView,
    onProfileClick,
    onReplyClick,
    onLike,
    onDelete,
    onModeratorDelete,
    onHide,
    onReport,
    onBlock,
    onUnblock,
    isCreator,
    isOriginalCreator,
    disablePortal = false,
    parentEntityType,
    ...restProps
  } = props;

  const { t } = useTranslation();

  const commentDate = createdAt ? new Date(createdAt) : undefined;
  const relativeDate =
    commentDate &&
    commentDate.getTime() - new Date(currentTime || Date.now()).getTime();

  const [showAllContent, setShowAllContent] = useState(false);
  const [hasOverflow, setHasOverflow] = useState(false);
  const handleShowAllContentToggle = useCallback(() => {
    setShowAllContent((prevShowAllContent) => !prevShowAllContent);
  }, []);
  const contentRef = useRef<HTMLDivElement>(null);
  useResizeObserver({
    ref: contentRef as React.RefObject<HTMLDivElement>, // https://github.com/juliencrn/usehooks-ts/pull/675
    onResize() {
      if (contentRef.current) {
        const clientHeight = contentRef.current.clientHeight + 1; // emoji compensation for Firefox (emojis adds 1px to the scrollHeight not accounted for by clientHeight, causing overflow)
        const scrollHeight = contentRef.current.scrollHeight;
        if (scrollHeight > clientHeight) {
          setHasOverflow(true);
        } else {
          if (!showAllContent) {
            setHasOverflow(false);
          }
        }
      }
    },
  });

  const { playbar: playbarStore, clips: clipsStore } = useStores();

  const clip = clipId ? clipsStore.clipById[clipId] : null;
  const duration = clip?.metadata?.duration;

  const goToTime = useCallback(
    (timestamp: number) => {
      logWebUserEvent({
        actionName: 'CommentTimestampClicked',
        context: {
          commentId: commentId,
          timestamp: timestamp,
        },
      });
      if (clip) {
        playbarStore.playClip(clip);
        playbarStore.userSetCurrentProgressWithTime(timestamp);
      }
    },
    [clip, playbarStore, commentId]
  );

  const annotations = useMemo(() => {
    const contentAnnotations: CommentContentAnnotation[] = [];

    if (userMentions) {
      for (const mention of userMentions) {
        contentAnnotations.push({
          render: (_value, key) => (
            <ProfileLink
              key={key}
              handle={mention.handle}
              className='font-bold text-foreground-primary'
            >
              {mention.displayName || `@${mention.handle}`}
            </ProfileLink>
          ),
          start: mention.start,
          end: mention.end,
        });
      }
    }

    // Only make timestamps clickable when viewing comments for clips
    if (duration && parentEntityType === 'clip') {
      let match: RegExpExecArray | null;
      const timestampMatchRegex = /(\b\d{1,2}:\d{2}\b)/g;
      while ((match = timestampMatchRegex.exec(content))) {
        const timestamp = decodeTimeFormat(match[0]);
        const start = match.index;
        const end = start + match[0].length;
        if (timestamp != null && timestamp <= duration) {
          contentAnnotations.push({
            render: (value, key) => (
              <button
                key={key}
                className='font-medium text-foreground-primary hover:underline'
                onClick={() => {
                  goToTime(timestamp);
                }}
              >
                {value}
              </button>
            ),
            start,
            end,
          });
        }
      }
    }

    contentAnnotations.sort((a, b) => {
      // Prioritize the one that starts first...
      if (a.start < b.start) return -1;
      if (a.start > b.start) return 1;
      // ...and deprioritize the one that ends last
      if (a.end < b.end) return -1;
      if (a.end > b.end) return 1;
      // Equivalent rank
      return 0;
    });

    return contentAnnotations;
  }, [content, userMentions, goToTime, duration]);

  return (
    <ListItem
      className={twMerge(
        clsx(
          'font-sans text-sm font-normal',
          'border-none text-foreground-secondary',
          'items-start px-4 py-1'
        ),
        className
      )}
      startClassName='basis-8 py-1'
      contentClassName='overflow-x-clip break-words'
      start={
        avatarUrl ? (
          <ProfileLink
            className='font-medium text-foreground-primary'
            href={profileHref}
            onClick={
              onProfileClick &&
              ((e) =>
                onProfileClick(
                  {
                    id: commentId,
                    type: entityType,
                    commentType: parentId ? 'reply' : 'comment',
                    clipId: clipId || undefined,
                    handle: handle || '',
                    context: 'avatar',
                  },
                  e
                ))
            }
            title={userCommentsBlocked ? t('profile.blocked') : undefined}
          >
            <Avatar
              displayName={displayName || handle || ''}
              handle={handle}
              src={avatarUrl}
              className='w-full'
            />
          </ProfileLink>
        ) : undefined
      }
      end={
        <CommentActions
          commentId={commentId}
          entityType={entityType}
          handle={handle}
          isLiked={isLiked}
          numLikes={numLikes}
          onLike={readonly ? undefined : onLike}
          onDelete={readonly ? undefined : onDelete}
          onModeratorDelete={readonly ? undefined : onModeratorDelete}
          onHide={readonly ? undefined : onHide}
          onReport={readonly ? undefined : onReport}
          onBlock={readonly ? undefined : onBlock}
          onUnblock={readonly ? undefined : onUnblock}
          disablePortal={disablePortal}
        />
      }
      {...restProps}
    >
      <div className='flex flex-col items-start justify-start gap-0.5'>
        <p>
          <ProfileLink
            className='font-medium text-foreground-primary'
            href={profileHref}
            onClick={
              onProfileClick &&
              ((e) =>
                onProfileClick(
                  {
                    id: commentId,
                    type: entityType,
                    commentType: parentId ? 'reply' : 'comment',
                    clipId: clipId || undefined,
                    handle: handle || '',
                    context: 'handle',
                  },
                  e
                ))
            }
          >
            {displayName || handle || ''}
          </ProfileLink>
          {isCreator && (
            <span className='mr-1 ml-1.5 inline-flex items-center rounded bg-gray-300 px-2 py-0.5 text-[13px] font-medium text-gray-950'>
              Creator
            </span>
          )}
          {isOriginalCreator && !isCreator && (
            <span className='mr-1 ml-1.5 inline-flex items-center rounded bg-gray-300 px-2 py-0.5 text-[13px] font-medium text-gray-950'>
              Original Creator
            </span>
          )}

          {typeof relativeDate === 'number' && (
            <>
              {' '}
              <span className='text-xs' title={commentDate?.toUTCString()}>
                {formatRelativeTime(relativeDate, 'narrow', t('time.justNow'))}
              </span>
            </>
          )}
          {/* Show timestamp for clip context, or entity label for hook context */}
          {parentEntityType === 'clip' && trackTimestamp != null && (
            <>
              {' '}
              <button
                className='mx-0.5 cursor-pointer text-xs font-bold text-strawberry-600 hover:underline'
                onClick={() => goToTime(trackTimestamp)}
              >
                at {encodeTimeFormat(trackTimestamp)}
              </button>
            </>
          )}
          {parentEntityType === 'hook' && (
            <>
              {' '}
              <span className='mx-0.5 text-xs font-bold text-strawberry-600'>
                {entityType === 'clip' ? 'on Song' : ''}
              </span>
            </>
          )}
        </p>
        <div
          className={twMerge(
            'self-stretch text-sm leading-tight',
            contentClassName
          )}
          style={
            contentLineClamp
              ? ({
                  '--comment-line-clamp': showAllContent
                    ? 'none'
                    : contentLineClamp,
                } as React.CSSProperties)
              : undefined
          }
          ref={contentRef}
        >
          {content &&
            formatCommentText(content, paragraphClassName, annotations)}
        </div>
        {hasOverflow && (
          <button
            className={clsx(
              'relative w-full',
              'flex flex-row items-center gap-1 text-xs font-medium text-foreground-inactive',
              {
                ['before:absolute before:inset-x-0 before:bottom-full before:block before:h-10 before:w-full before:bg-linear-to-t before:from-(--comment-bg,var(--color-gray-50))']:
                  !showAllContent,
              }
            )}
            onClick={handleShowAllContentToggle}
          >
            <span>
              {showAllContent ? t('actions.showLess') : t('actions.showMore')}
            </span>
            {showAllContent ? (
              <ChevronUpIcon className='h-2 w-2 text-current' />
            ) : (
              <ChevronDownIcon className='h-2 w-2 text-current' />
            )}
          </button>
        )}
        {children}
        {onReplyClick && (
          <div>
            <button
              className='cursor-pointer text-sm font-medium text-foreground-inactive hover:underline'
              onClick={onReplyClick}
            >
              {t('comments.reply')}
            </button>
          </div>
        )}
      </div>
    </ListItem>
  );
};

export default memo(CommentListItem);
