/* eslint-disable react/destructuring-assignment */
import React, { MouseEventHandler, useMemo } from 'react';
import { IEventWithRoomId, JoinRule, RelationType, Room } from 'matrix-js-sdk';
import { HTMLReactParserOptions } from 'html-react-parser';
import { Avatar, Box, Chip, Header, Text, config } from 'folds';
import { Icon, Icons } from '../../components/icons';
import { Opts as LinkifyOpts } from 'linkifyjs';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import {
  factoryRenderLinkifyWithMention,
  getReactCustomHtmlParser,
  LINKIFY_OPTS,
  makeHighlightRegex,
  makeMentionCustomProps,
  renderMatrixMention,
} from '../../plugins/react-custom-html-parser';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
import { useMatrixEventRenderer } from '../../hooks/useMatrixEventRenderer';
import { GetContentCallback, MessageEvent, StateEvent } from '../../../types/matrix/room';
import {
  AvatarBase,
  ImageContent,
  MSticker,
  ModernLayout,
  RedactedContent,
  Reply,
  Time,
  Username,
  UsernameBold,
} from '../../components/message';
import { RenderMessageContent } from '../../components/RenderMessageContent';
import { Image } from '../../components/media';
import { ImageViewer } from '../../components/image-viewer';
import * as customHtmlCss from '../../styles/CustomHtml.css';
import { RoomAvatar, RoomIcon } from '../../components/room-avatar';
import { getMemberAvatarMxc, getMemberDisplayName, getRoomAvatarUrl } from '../../utils/room';
import { ResultItem } from './useMessageSearch';
import { SequenceCard } from '../../components/sequence-card';
import { UserAvatar } from '../../components/user-avatar';
import { useMentionClickHandler } from '../../hooks/useMentionClickHandler';
import { useSpoilerClickHandler } from '../../hooks/useSpoilerClickHandler';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { usePowerLevels } from '../../hooks/usePowerLevels';
import { usePowerLevelTags } from '../../hooks/usePowerLevelTags';
import { useTheme } from '../../hooks/useTheme';
import { PowerIcon } from '../../components/power';
import colorMXID from '../../../util/colorMXID';
import {
  getPowerTagIconSrc,
  useAccessiblePowerTagColors,
  useGetMemberPowerTag,
} from '../../hooks/useMemberPowerTag';
import { useRoomCreators } from '../../hooks/useRoomCreators';
import { useRoomCreatorsTag } from '../../hooks/useRoomCreatorsTag';
import { useOtherUserColor } from '../../hooks/useUserColor';

/**
 * Props for SearchResultItem component
 */
type SearchResultItemProps = {
  room: Room;
  item: ResultItem;
  getMemberPowerTag: ReturnType<typeof useGetMemberPowerTag>;
  accessibleTagColors: Map<string, string> | undefined;
  renderMatrixEvent: ReturnType<typeof useMatrixEventRenderer<[IEventWithRoomId, string, GetContentCallback]>>;
  handleOpenClick: MouseEventHandler;
  legacyUsernameColor?: boolean;
  hour24Clock: boolean;
  dateFormatString: string;
};

/**
 * Component to render a single search result item with proper hook usage
 */
function SearchResultItem({
  room,
  item,
  getMemberPowerTag,
  accessibleTagColors,
  renderMatrixEvent,
  handleOpenClick,
  legacyUsernameColor,
  hour24Clock,
  dateFormatString,
}: SearchResultItemProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const { event } = item;

  const displayName =
    getMemberDisplayName(room, event.sender) ??
    getMxIdLocalPart(event.sender) ??
    event.sender;
  const senderAvatarMxc = getMemberAvatarMxc(room, event.sender);

  const relation = event.content['m.relates_to'];
  const mainEventId =
    relation?.rel_type === RelationType.Replace ? relation.event_id : event.event_id;

  const getContent = (() =>
    event.content['m.new_content'] ?? event.content) as GetContentCallback;

  const replyEventId = relation?.['m.in_reply_to']?.event_id;
  const threadRootId =
    relation?.rel_type === RelationType.Thread ? relation.event_id : undefined;

  // Get custom user color from avatar metadata
  const customUserColor = useOtherUserColor(event.sender, senderAvatarMxc);

  const memberPowerTag = getMemberPowerTag(event.sender);
  const tagColor = memberPowerTag?.color
    ? accessibleTagColors?.get(memberPowerTag.color)
    : undefined;
  const tagIconSrc = memberPowerTag?.icon
    ? getPowerTagIconSrc(mx, useAuthentication, memberPowerTag.icon)
    : undefined;

  // Priority: custom user color > tag color (non-legacy) > colorMXID (legacy)
  const usernameColor = customUserColor ?? (legacyUsernameColor ? colorMXID(event.sender) : tagColor);

  return (
    <SequenceCard
      key={event.event_id}
      style={{ padding: config.space.S400 }}
      variant="SurfaceVariant"
      direction="Column"
    >
      <ModernLayout
        before={
          <AvatarBase>
            <Avatar size="300">
              <UserAvatar
                userId={event.sender}
                src={
                  senderAvatarMxc
                    ? mxcUrlToHttp(
                        mx,
                        senderAvatarMxc,
                        useAuthentication,
                        48,
                        48,
                        'crop'
                      ) ?? undefined
                    : undefined
                }
                alt={displayName}
                renderFallback={() => <Icon size="200" src={Icons.User} filled />}
              />
            </Avatar>
          </AvatarBase>
        }
      >
        <Box gap="300" justifyContent="SpaceBetween" alignItems="Center" grow="Yes">
          <Box gap="200" alignItems="Baseline">
            <Box alignItems="Center" gap="200">
              <Username style={{ color: usernameColor }}>
                <Text as="span" truncate>
                  <UsernameBold>{displayName}</UsernameBold>
                </Text>
              </Username>
              {tagIconSrc && <PowerIcon size="100" iconSrc={tagIconSrc} />}
            </Box>
            <Time
              ts={event.origin_server_ts}
              hour24Clock={hour24Clock}
              dateFormatString={dateFormatString}
            />
          </Box>
          <Box shrink="No" gap="200" alignItems="Center">
            <Chip
              data-event-id={mainEventId}
              onClick={handleOpenClick}
              variant="Secondary"
              radii="400"
            >
              <Text size="T200">Open</Text>
            </Chip>
          </Box>
        </Box>
        {replyEventId && (
          <Reply
            room={room}
            replyEventId={replyEventId}
            threadRootId={threadRootId}
            onClick={handleOpenClick}
            getMemberPowerTag={getMemberPowerTag}
            accessibleTagColors={accessibleTagColors}
            legacyUsernameColor={legacyUsernameColor}
          />
        )}
        {renderMatrixEvent(event.type, false, event, displayName, getContent)}
      </ModernLayout>
    </SequenceCard>
  );
}

type SearchResultGroupProps = {
  room: Room;
  highlights: string[];
  items: ResultItem[];
  mediaAutoLoad?: boolean;
  urlPreview?: boolean;
  onOpen: (roomId: string, eventId: string) => void;
  legacyUsernameColor?: boolean;
  hour24Clock: boolean;
  dateFormatString: string;
};
export function SearchResultGroup({
  room,
  highlights,
  items,
  mediaAutoLoad,
  urlPreview,
  onOpen,
  legacyUsernameColor,
  hour24Clock,
  dateFormatString,
}: SearchResultGroupProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const highlightRegex = useMemo(() => makeHighlightRegex(highlights), [highlights]);

  const powerLevels = usePowerLevels(room);
  const creators = useRoomCreators(room);

  const creatorsTag = useRoomCreatorsTag();
  const powerLevelTags = usePowerLevelTags(room, powerLevels);
  const getMemberPowerTag = useGetMemberPowerTag(room, creators, powerLevels);

  const theme = useTheme();
  const accessibleTagColors = useAccessiblePowerTagColors(theme.kind, creatorsTag, powerLevelTags);

  const mentionClickHandler = useMentionClickHandler(room.roomId);
  const spoilerClickHandler = useSpoilerClickHandler();

  const linkifyOpts = useMemo<LinkifyOpts>(
    () => ({
      ...LINKIFY_OPTS,
      render: factoryRenderLinkifyWithMention((href) =>
        renderMatrixMention(mx, room.roomId, href, makeMentionCustomProps(mentionClickHandler))
      ),
    }),
    [mx, room, mentionClickHandler]
  );
  const htmlReactParserOptions = useMemo<HTMLReactParserOptions>(
    () =>
      getReactCustomHtmlParser(mx, room.roomId, {
        linkifyOpts,
        highlightRegex,
        useAuthentication,
        handleSpoilerClick: spoilerClickHandler,
        handleMentionClick: mentionClickHandler,
      }),
    [
      mx,
      room,
      linkifyOpts,
      highlightRegex,
      mentionClickHandler,
      spoilerClickHandler,
      useAuthentication,
    ]
  );

  const renderMatrixEvent = useMatrixEventRenderer<[IEventWithRoomId, string, GetContentCallback]>(
    {
      [MessageEvent.RoomMessage]: (event, displayName, getContent) => {
        if (event.unsigned?.redacted_because) {
          return <RedactedContent reason={event.unsigned?.redacted_because.content.reason} />;
        }

        return (
          <RenderMessageContent
            displayName={displayName}
            msgType={event.content.msgtype ?? ''}
            ts={event.origin_server_ts}
            getContent={getContent}
            mediaAutoLoad={mediaAutoLoad}
            urlPreview={urlPreview}
            htmlReactParserOptions={htmlReactParserOptions}
            linkifyOpts={linkifyOpts}
            highlightRegex={highlightRegex}
            outlineAttachment
          />
        );
      },
      [MessageEvent.Reaction]: (event, displayName, getContent) => {
        if (event.unsigned?.redacted_because) {
          return <RedactedContent reason={event.unsigned?.redacted_because.content.reason} />;
        }
        return (
          <MSticker
            content={getContent()}
            renderImageContent={(props) => (
              <ImageContent
                {...props}
                autoPlay={mediaAutoLoad}
                renderImage={(p) => <Image {...p} loading="lazy" />}
                renderViewer={(p) => <ImageViewer {...p} />}
              />
            )}
          />
        );
      },
      [StateEvent.RoomTombstone]: (event) => {
        const { content } = event;
        return (
          <Box grow="Yes" direction="Column">
            <Text size="T400" priority="300">
              Room Tombstone. {content.body}
            </Text>
          </Box>
        );
      },
    },
    undefined,
    (event) => {
      if (event.unsigned?.redacted_because) {
        return <RedactedContent reason={event.unsigned?.redacted_because.content.reason} />;
      }
      return (
        <Box grow="Yes" direction="Column">
          <Text size="T400" priority="300">
            <code className={customHtmlCss.Code}>{event.type}</code>
            {' event'}
          </Text>
        </Box>
      );
    }
  );

  const handleOpenClick: MouseEventHandler = (evt) => {
    const eventId = evt.currentTarget.getAttribute('data-event-id');
    if (!eventId) return;
    onOpen(room.roomId, eventId);
  };

  return (
    <Box direction="Column" gap="200">
      <Header size="300">
        <Box gap="200" grow="Yes">
          <Avatar size="200" radii="300">
            <RoomAvatar
              roomId={room.roomId}
              src={getRoomAvatarUrl(mx, room, 96, useAuthentication)}
              alt={room.name}
              renderFallback={() => (
                <RoomIcon size="50" joinRule={room.getJoinRule() ?? JoinRule.Restricted} filled />
              )}
            />
          </Avatar>
          <Text size="H4" truncate>
            {room.name}
          </Text>
        </Box>
      </Header>
      <Box direction="Column" gap="100">
        {items.map((item) => (
          <SearchResultItem
            key={item.event.event_id}
            room={room}
            item={item}
            getMemberPowerTag={getMemberPowerTag}
            accessibleTagColors={accessibleTagColors}
            renderMatrixEvent={renderMatrixEvent}
            handleOpenClick={handleOpenClick}
            legacyUsernameColor={legacyUsernameColor}
            hour24Clock={hour24Clock}
            dateFormatString={dateFormatString}
          />
        ))}
      </Box>
    </Box>
  );
}
