import React, { ChangeEvent, useCallback, useMemo, useRef, useState } from 'react';
import FocusTrap from 'focus-trap-react';
import {
  Avatar,
  Box,
  Button,
  Checkbox,
  Chip,
  color,
  config,
  Header,
  Icon,
  IconButton,
  Icons,
  Input,
  Line,
  MenuItem,
  Modal,
  Overlay,
  OverlayBackdrop,
  OverlayCenter,
  Scroll,
  Spinner,
  Text,
  toRem,
} from 'folds';
import { MatrixEvent, MsgType, Room } from 'matrix-js-sdk';
import { useAtomValue, useAtom } from 'jotai';
import { IThumbnailContent } from '../../../../types/matrix/common';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { stopPropagation } from '../../../utils/keyboard';
import { useModalStyle } from '../../../hooks/useModalStyle';
import { mDirectAtom } from '../../../state/mDirectList';
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
import { mxcUrlToHttp } from '../../../utils/matrix';
import { RoomAvatar, RoomIcon } from '../../../components/room-avatar';
import { UserAvatar } from '../../../components/user-avatar';
import { ThumbnailContent } from '../../../components/message/content/ThumbnailContent';
import { getMemberAvatarMxc, getMemberName, trimReplyFromBody } from '../../../utils/room';
import { nameInitials } from '../../../utils/common';
import {
  recentForwardTargetsAtom,
  addRecentForwardTarget,
} from '../../../state/recentForwardTargets';
import { buildForwardContent } from './forwardContent';

type RoomRowProps = {
  room: Room;
  dm: boolean;
  useAuthentication: boolean;
  selected: boolean;
  onToggle: () => void;
  sending: boolean;
};
function RoomRow({ room, dm, useAuthentication, selected, onToggle, sending }: RoomRowProps) {
  const mx = useMatrixClient();
  const avatarMxc = room.getMxcAvatarUrl();
  const avatarUrl = avatarMxc
    ? (mxcUrlToHttp(mx, avatarMxc, useAuthentication, 48, 48, 'crop') ?? undefined)
    : undefined;

  return (
    <MenuItem
      size="300"
      radii="300"
      onClick={onToggle}
      disabled={sending}
      after={
        <Checkbox
          checked={selected}
          readOnly
          variant="Primary"
          disabled={sending}
          onClick={(evt) => {
            evt.stopPropagation();
            onToggle();
          }}
        />
      }
      before={
        <Avatar size="200" radii="300">
          <RoomAvatar
            roomId={room.roomId}
            src={avatarUrl}
            alt={room.name}
            renderFallback={() => (
              <RoomIcon roomType={room.getType()} size="100" joinRule={room.getJoinRule()} filled />
            )}
          />
        </Avatar>
      }
    >
      <Box direction="Column">
        <Text size="T300" truncate>
          {room.name}
        </Text>
        {dm && (
          <Text size="T200" priority="300" truncate>
            Direct Message
          </Text>
        )}
      </Box>
    </MenuItem>
  );
}

// Compact, read-only preview of the message being forwarded — sender + body,
// plus a small thumbnail for image/video. We already hold mEvent, so no fetch.
function ForwardPreview({
  mEvent,
  useAuthentication,
}: {
  mEvent: MatrixEvent;
  useAuthentication: boolean;
}) {
  const mx = useMatrixClient();
  const room = mx.getRoom(mEvent.getRoomId() ?? '') ?? undefined;
  const senderId = mEvent.getSender() ?? '';
  const senderName = room
    ? getMemberName(room, senderId)
    : senderId.split(':')[0]?.slice(1) || senderId;
  const senderMxc = room ? getMemberAvatarMxc(room, senderId) : undefined;
  const senderAvatarUrl = senderMxc
    ? (mxcUrlToHttp(mx, senderMxc, useAuthentication, 48, 48, 'crop') ?? undefined)
    : undefined;

  const content = mEvent.getContent();
  const msgtype = content.msgtype;
  const isMedia =
    msgtype === MsgType.Image ||
    msgtype === MsgType.Video ||
    msgtype === MsgType.File ||
    msgtype === MsgType.Audio;
  const bodyStr = typeof content.body === 'string' ? content.body : '';
  const label = isMedia
    ? ((content.filename as string | undefined) ?? bodyStr) || '(media)'
    : trimReplyFromBody(bodyStr) || '(message)';
  const info = content.info as IThumbnailContent | undefined;
  const showThumb =
    (msgtype === MsgType.Image || msgtype === MsgType.Video) &&
    !!info &&
    (!!info.thumbnail_url || !!info.thumbnail_file);

  return (
    <Box
      shrink="No"
      gap="200"
      alignItems="Center"
      role="group"
      aria-label="Message to forward"
      style={{
        margin: `${config.space.S200} ${config.space.S400} 0`,
        padding: config.space.S200,
        borderRadius: config.radii.R300,
        background: color.SurfaceVariant.Container,
      }}
    >
      <Avatar size="200" radii="300">
        <UserAvatar
          userId={senderId}
          src={senderAvatarUrl}
          alt={senderName}
          renderFallback={() => <Text size="H6">{nameInitials(senderName)}</Text>}
        />
      </Avatar>
      {showThumb && info && (
        <ThumbnailContent
          info={info}
          renderImage={(src) => (
            <img
              src={src}
              alt=""
              style={{
                width: toRem(40),
                height: toRem(40),
                borderRadius: config.radii.R300,
                objectFit: 'cover',
                flexShrink: 0,
              }}
            />
          )}
        />
      )}
      <Box direction="Column" grow="Yes" style={{ minWidth: 0 }}>
        <Text size="T200" truncate style={{ fontWeight: config.fontWeight.W600 }}>
          {senderName}
        </Text>
        <Text size="T200" priority="300" truncate>
          {label}
        </Text>
      </Box>
    </Box>
  );
}

// A compact selectable chip for a recently-forwarded-to room.
function RecentChip({
  room,
  useAuthentication,
  selected,
  onToggle,
  sending,
}: {
  room: Room;
  useAuthentication: boolean;
  selected: boolean;
  onToggle: () => void;
  sending: boolean;
}) {
  const mx = useMatrixClient();
  const avatarMxc = room.getMxcAvatarUrl();
  const avatarUrl = avatarMxc
    ? (mxcUrlToHttp(mx, avatarMxc, useAuthentication, 48, 48, 'crop') ?? undefined)
    : undefined;

  return (
    <Chip
      variant={selected ? 'Primary' : 'SurfaceVariant'}
      fill="Soft"
      radii="Pill"
      disabled={sending}
      onClick={onToggle}
      aria-pressed={selected}
      before={
        <Avatar size="200" radii="300">
          <RoomAvatar
            roomId={room.roomId}
            src={avatarUrl}
            alt={room.name}
            renderFallback={() => (
              <RoomIcon roomType={room.getType()} size="100" joinRule={room.getJoinRule()} filled />
            )}
          />
        </Avatar>
      }
    >
      <Text size="B300" truncate style={{ maxWidth: toRem(120) }}>
        {room.name}
      </Text>
    </Chip>
  );
}

type Props = {
  mEvent: MatrixEvent;
  onClose: () => void;
};

export function ForwardMessageDialog({ mEvent, onClose }: Props) {
  const mx = useMatrixClient();
  const modalStyle = useModalStyle(400);
  const directs = useAtomValue(mDirectAtom);
  const useAuthentication = useMediaAuthentication();
  const searchInputRef = useRef<HTMLInputElement>(null);
  const [query, setQuery] = useState('');
  const [comment, setComment] = useState('');
  const [sending, setSending] = useState(false);
  const [sentTo, setSentTo] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [recents, setRecents] = useAtom(recentForwardTargetsAtom);
  // Rooms whose comment message already delivered this session — so a retry after
  // a forward failure doesn't re-post the comment (only the missing forward). Kept
  // for the dialog's lifetime: a room that already got the comment won't get it
  // again even if the text is later edited, which is the safe (no-duplicate) choice.
  const commentSentRef = useRef<Set<string>>(new Set());
  // Selection persists across query changes: a room selected then filtered out
  // of the rendered slice stays selected.
  const [selectedRoomIds, setSelectedRoomIds] = useState<Set<string>>(new Set());

  const toggleRoom = useCallback((roomId: string) => {
    setSelectedRoomIds((prev) => {
      const next = new Set(prev);
      if (next.has(roomId)) {
        next.delete(roomId);
      } else {
        next.add(roomId);
      }
      return next;
    });
  }, []);

  const allRooms = useMemo(
    () =>
      mx
        .getRooms()
        .filter((r) => r.getMyMembership() === 'join' && !r.isSpaceRoom())
        .sort((a, b) => (b.getLastActiveTimestamp() ?? 0) - (a.getLastActiveTimestamp() ?? 0)),
    [mx],
  );

  const filtered = useMemo(() => {
    if (!query) return allRooms;
    const q = query.toLowerCase();
    return allRooms.filter((r) => r.name.toLowerCase().includes(q));
  }, [allRooms, query]);

  // Resolve recent target ids to still-joined rooms (drop ones left/gone).
  const recentRooms = useMemo(
    () =>
      recents
        .map((id) => mx.getRoom(id))
        .filter((r): r is Room => !!r && r.getMyMembership() === 'join' && !r.isSpaceRoom()),
    [recents, mx],
  );

  const sendToSelected = useCallback(async () => {
    if (sending || selectedRoomIds.size === 0) return;
    const fwdContent = buildForwardContent(mx, mEvent);
    if (!fwdContent) {
      setError('This message could not be decrypted, so it cannot be forwarded.');
      return;
    }
    setSending(true);
    setError(null);

    const ids = [...selectedRoomIds];
    const commentBody = comment.trim();
    const results = await Promise.allSettled(
      ids.map((id) => {
        // threadId-aware overload (P3-8): explicit null = send to the main timeline.
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const sendForward = () => mx.sendEvent(id, null, mEvent.getType() as any, fwdContent);
        // Send the optional comment first so it reads as a note above the
        // forwarded content. The room counts as failed if either send rejects.
        // Track rooms whose comment already landed so a retry (after the FORWARD
        // failed) doesn't post the comment twice — only the missing forward.
        const needsComment = commentBody && !commentSentRef.current.has(id);
        const step = needsComment
          ? mx
              // eslint-disable-next-line @typescript-eslint/no-explicit-any
              .sendMessage(id, null, { msgtype: MsgType.Text, body: commentBody } as any)
              .then(() => {
                commentSentRef.current.add(id);
              })
          : Promise.resolve();
        return step.then(sendForward);
      }),
    );

    const failedIds: string[] = [];
    const failedNames: string[] = [];
    const succeededIds: string[] = [];
    results.forEach((result, i) => {
      if (result.status === 'rejected') {
        failedIds.push(ids[i]);
        failedNames.push(mx.getRoom(ids[i])?.name ?? ids[i]);
      } else {
        succeededIds.push(ids[i]);
      }
    });
    // Remember successful targets (most-recent first) for the Recent row.
    succeededIds.forEach((id) => setRecents((prev) => addRecentForwardTarget(prev, id)));

    const total = ids.length;
    const failed = failedNames.length;
    const succeeded = total - failed;

    if (failed === 0) {
      setSentTo(`Forwarded to ${total} ${total === 1 ? 'room' : 'rooms'}`);
      setTimeout(onClose, 1400);
      return;
    }

    setSending(false);
    // Prune to only the failures so a retry doesn't re-send to rooms that
    // already succeeded (duplicate messages).
    setSelectedRoomIds(new Set(failedIds));
    if (succeeded === 0) {
      setError('Failed to forward. Try again.');
      return;
    }
    setError(`Forwarded to ${succeeded}/${total}. Failed: ${failedNames.join(', ')}.`);
  }, [mx, mEvent, onClose, sending, selectedRoomIds, comment, setRecents]);

  return (
    <Overlay open backdrop={<OverlayBackdrop />}>
      <OverlayCenter>
        <FocusTrap
          focusTrapOptions={{
            initialFocus: () => searchInputRef.current ?? false,
            onDeactivate: onClose,
            clickOutsideDeactivates: true,
            escapeDeactivates: stopPropagation,
          }}
        >
          <Modal
            size="400"
            style={{
              maxHeight: '480px',
              borderRadius: config.radii.R500,
              display: 'flex',
              flexDirection: 'column',
              ...modalStyle,
            }}
          >
            <Header
              variant="Surface"
              size="500"
              style={{ padding: `0 ${config.space.S200} 0 ${config.space.S400}` }}
            >
              <Box grow="Yes">
                <Text as="h2" size="H4" truncate>
                  Forward message
                </Text>
              </Box>
              <IconButton size="300" onClick={onClose} radii="300" aria-label="Close">
                <Icon src={Icons.Cross} />
              </IconButton>
            </Header>
            {!sentTo && <ForwardPreview mEvent={mEvent} useAuthentication={useAuthentication} />}
            {!sentTo && (
              <Box
                shrink="No"
                direction="Column"
                gap="200"
                style={{ padding: `${config.space.S200} ${config.space.S400}` }}
              >
                <Input
                  ref={searchInputRef}
                  variant="Background"
                  size="400"
                  radii="400"
                  outlined
                  aria-label="Search rooms"
                  placeholder="Search rooms…"
                  value={query}
                  onChange={(e: ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)}
                />
                <Input
                  variant="Background"
                  size="400"
                  radii="400"
                  outlined
                  aria-label="Add a comment"
                  placeholder="Add a comment (optional)…"
                  value={comment}
                  disabled={sending}
                  onChange={(e: ChangeEvent<HTMLInputElement>) => setComment(e.target.value)}
                />
                {error && (
                  <Text size="T200" style={{ color: color.Critical.Main }}>
                    {error}
                  </Text>
                )}
              </Box>
            )}
            <Line size="300" />
            {sentTo ? (
              <Box
                grow="Yes"
                alignItems="Center"
                justifyContent="Center"
                gap="300"
                style={{ padding: config.space.S400 }}
              >
                <Text size="T300">✓ {sentTo}</Text>
              </Box>
            ) : (
              <>
                <Box grow="Yes" style={{ minHeight: 0, position: 'relative' }}>
                  <Scroll size="300" hideTrack visibility="Hover">
                    <Box
                      direction="Column"
                      gap="100"
                      style={{ padding: config.space.S200, opacity: sending ? 0.5 : 1 }}
                    >
                      {/* Recent targets — quick access, hidden while searching */}
                      {!query && recentRooms.length > 0 && (
                        <Box
                          direction="Column"
                          gap="100"
                          style={{ paddingBottom: config.space.S100 }}
                        >
                          <Text
                            size="L400"
                            priority="300"
                            style={{ padding: `0 ${config.space.S200}` }}
                          >
                            Recent
                          </Text>
                          <Box
                            gap="100"
                            wrap="Wrap"
                            role="group"
                            aria-label="Recent forward targets"
                            style={{ padding: `0 ${config.space.S200}` }}
                          >
                            {recentRooms.map((room) => (
                              <RecentChip
                                key={room.roomId}
                                room={room}
                                useAuthentication={useAuthentication}
                                selected={selectedRoomIds.has(room.roomId)}
                                onToggle={() => toggleRoom(room.roomId)}
                                sending={sending}
                              />
                            ))}
                          </Box>
                          <Line size="300" style={{ marginTop: config.space.S100 }} />
                        </Box>
                      )}
                      {filtered.slice(0, 60).map((room) => (
                        <RoomRow
                          key={room.roomId}
                          room={room}
                          dm={directs.has(room.roomId)}
                          useAuthentication={useAuthentication}
                          selected={selectedRoomIds.has(room.roomId)}
                          onToggle={() => toggleRoom(room.roomId)}
                          sending={sending}
                        />
                      ))}
                      {filtered.length === 0 && (
                        <Box
                          alignItems="Center"
                          justifyContent="Center"
                          style={{ padding: config.space.S400 }}
                        >
                          <Text size="T300" priority="300">
                            No rooms found
                          </Text>
                        </Box>
                      )}
                    </Box>
                  </Scroll>
                  {sending && (
                    <Box
                      alignItems="Center"
                      justifyContent="Center"
                      style={{ position: 'absolute', inset: 0 }}
                    >
                      <Spinner variant="Secondary" size="400" />
                    </Box>
                  )}
                </Box>
                <Line size="300" />
                <Box
                  shrink="No"
                  direction="Column"
                  style={{ padding: `${config.space.S200} ${config.space.S400}` }}
                >
                  <Button
                    variant="Primary"
                    size="400"
                    radii="400"
                    disabled={selectedRoomIds.size === 0 || sending}
                    before={sending && <Spinner variant="Primary" fill="Solid" size="200" />}
                    onClick={sendToSelected}
                  >
                    <Text size="B400">
                      Send to {selectedRoomIds.size} {selectedRoomIds.size === 1 ? 'room' : 'rooms'}
                    </Text>
                  </Button>
                </Box>
              </>
            )}
          </Modal>
        </FocusTrap>
      </OverlayCenter>
    </Overlay>
  );
}
