import React, { type MouseEventHandler, type ReactNode } from 'react';
import classNames from 'classnames';
import { Avatar, Box, Icon, Icons, type IconSrc, as } from 'folds';
import { type Room } from 'matrix-js-sdk';

import * as css from './Channel.css';
import { CHANNEL_AVATAR_PX } from './Channel.css';
import { UserAvatar } from '../../user-avatar';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { getMemberAvatarMxc } from '../../../utils/room';
import { mxcUrlToHttp } from '../../../utils/matrix';

// MessageBase recipe `space` variant for channel rows. '300' tightens the
// vertical rhythm vs the '400' Stream uses for bubble rows — channel
// rows have no bubble border, so they don't need as much breathing room.
export const CHANNEL_MESSAGE_SPACING = '300' as const;

export type ChannelLayoutProps = {
  // Avatar slot — pass `undefined` for collapsed rows (adjacent same-user)
  // so the slot stays a fixed-width spacer and the body column aligns
  // across all rows in the cluster.
  avatar?: ReactNode;
  // Username + time inline. `undefined` on collapsed rows.
  header?: ReactNode;
  reactions?: ReactNode;
  threadSummary?: ReactNode;
  // Forwarded onto the row root as `data-own="true"|"false"`. Channels
  // main timeline doesn't style off it; the thread-drawer bubble CSS
  // reads it to mirror `StreamBubble`'s own-vs-incoming notch corner.
  isOwn?: boolean;
  // `true` (thread drawer): the header (name + time) renders INSIDE the body
  // slot above the content, and the body is a chat bubble — the compact
  // in-bubble look.
  // `false` (Discord-style main timeline): the header renders as a sibling row
  // ABOVE the body (next to the avatar) and the body is plain text (no bubble)
  // for everyone — Discord groups don't bubble messages. See `data-bubble`
  // below and `Channel.css.ts`.
  headerInBubble?: boolean;
  onContextMenu?: MouseEventHandler<HTMLDivElement>;
};

// Channels timeline message row primitive. No rail. No bubble. Avatar
// + body two-column flex; body holds header (name + time), content,
// thread-summary, reactions in vertical flow.
export const ChannelLayout = as<'div', ChannelLayoutProps>(
  (
    {
      className,
      avatar,
      header,
      reactions,
      threadSummary,
      isOwn,
      headerInBubble,
      onContextMenu,
      children,
      ...props
    },
    ref
  ) => (
    <div
      className={classNames(css.ChannelRow, className)}
      onContextMenu={onContextMenu}
      data-own={isOwn ? 'true' : 'false'}
      // Bubbles only in the compact in-bubble mode (thread drawer). The Discord
      // header-above main timeline renders ALL messages as plain text (no
      // bubbles) — own and peer alike.
      data-bubble={headerInBubble ? 'true' : undefined}
      {...props}
      ref={ref}
    >
      <div className={css.ChannelAvatarSlot}>{avatar}</div>
      <div className={css.ChannelBody}>
        {!headerInBubble && header && <div className={css.ChannelHeader}>{header}</div>}
        <div className={css.ChannelMessageBody}>
          {headerInBubble && header && (
            <div className={css.ChannelHeader} data-in-bubble="true">
              {header}
            </div>
          )}
          {children}
          {headerInBubble && threadSummary && (
            // Inside-bubble footer: stretches via negative margins to the
            // bubble's inner border edge, paints a 1px top divider, and
            // hosts the existing thread-summary button as a flush
            // full-width chip. Reads as one continuous card with a
            // section break instead of two stacked pills.
            <div className={css.ChannelBubbleThreadSummary}>{threadSummary}</div>
          )}
        </div>
        {!headerInBubble && threadSummary && (
          <div className={css.ChannelThreadSummary}>{threadSummary}</div>
        )}
        {reactions && <div className={css.ChannelReactions}>{reactions}</div>}
      </div>
    </div>
  )
);

export type ChannelDayDividerProps = {
  label: ReactNode;
};

// Section break between days in the channels timeline. Horizontal line
// + centered uppercase label, spans full row including the avatar gutter
// so it reads as a structural separator.
export const ChannelDayDivider = as<'div', ChannelDayDividerProps>(
  ({ className, label, ...props }, ref) => (
    <div
      className={classNames(css.ChannelDayDividerRoot, className)}
      role="separator"
      aria-label={typeof label === 'string' ? label : undefined}
      {...props}
      ref={ref}
    >
      <span className={css.ChannelDayDividerLine} aria-hidden />
      <span className={css.ChannelDayDividerLabel}>{label}</span>
      <span className={css.ChannelDayDividerLine} aria-hidden />
    </div>
  )
);

export type ChannelEventContentProps = {
  iconSrc: IconSrc;
  content: ReactNode;
};

// Sysline (membership change / room.create / pinned events / etc) row
// for channels. Single-line, indented past the avatar slot so it visually
// belongs to the body column, no rail/timestamp chrome.
export function ChannelEventContent({ iconSrc, content }: ChannelEventContentProps) {
  return (
    <Box className={css.ChannelSysline} alignItems="Center" gap="200">
      <Icon className={css.ChannelSyslineIcon} size="50" src={iconSrc} />
      <div className={css.ChannelSyslineBody}>{content}</div>
    </Box>
  );
}

export type ChannelMessageAvatarProps = {
  room: Room;
  senderId: string;
  senderDisplayName: string;
};

// Resolves the sender avatar (with auth + thumbnail) and renders the
// folds `<Avatar>` + `<UserAvatar>` combination. Lifted out of `Message`
// so the `useMediaAuthentication` / `useMatrixClient` hook calls only run
// when channel layout is selected (Stream rows don't need an avatar).
export function ChannelMessageAvatar({
  room,
  senderId,
  senderDisplayName,
}: ChannelMessageAvatarProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const avatarMxc = getMemberAvatarMxc(room, senderId);
  const avatarUrl = avatarMxc
    ? mxcUrlToHttp(mx, avatarMxc, useAuthentication, 96, 96, 'crop') ?? undefined
    : undefined;
  return (
    <Avatar size="300" style={{ width: CHANNEL_AVATAR_PX, height: CHANNEL_AVATAR_PX }}>
      <UserAvatar
        userId={senderId}
        src={avatarUrl}
        alt={senderDisplayName}
        renderFallback={() => <Icon size="100" src={Icons.User} filled />}
      />
    </Avatar>
  );
}
