import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
import { Box, Button, Icon, Icons, Text, color, toRem } from 'folds';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useNavigate } from 'react-router-dom';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { factoryRoomIdByActivity } from '../../../utils/sort';
import { NavCategory, NavEmptyCenter, NavEmptyLayout } from '../../../components/nav';
import { getDirectCreatePath, getDirectRoomPath } from '../../pathUtils';
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
import { VirtualTile } from '../../../components/virtualizer';
import { DirectInviteRow, DmStreamRow } from '../../../features/room-nav';
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
import { useDirectRooms } from './useDirectRooms';
import { useDirectInvites, DirectInviteEntry } from './useDirectInvites';
import { PageNav, PageNavContent } from '../../../components/page';
import {
  getRoomNotificationMode,
  useRoomsNotificationPreferencesContext,
} from '../../../hooks/useRoomsNotificationPreferences';
import { DirectStreamHeader } from './DirectStreamHeader';
import { DirectNewChatRow } from './DirectNewChatRow';
import { DirectSelfRow } from './DirectSelfRow';

type ListItem =
  | { kind: 'invite'; entry: DirectInviteEntry }
  | { kind: 'spam-toggle'; spamCount: number; expanded: boolean }
  | { kind: 'spam-invite'; entry: DirectInviteEntry }
  | { kind: 'direct'; roomId: string };

function DirectEmpty() {
  const { t } = useTranslation();
  const navigate = useNavigate();

  return (
    <NavEmptyCenter>
      <NavEmptyLayout
        icon={<Icon size="600" src={Icons.Mention} />}
        title={
          <Text size="H5" align="Center">
            {t('Direct.no_direct_messages')}
          </Text>
        }
        content={
          <Text size="T300" align="Center">
            {t('Direct.no_direct_messages_desc')}
          </Text>
        }
        options={
          <Button variant="Primary" size="300" onClick={() => navigate(getDirectCreatePath())}>
            <Text size="B300" truncate>
              {t('Direct.start_first_chat')}
            </Text>
          </Button>
        }
      />
    </NavEmptyCenter>
  );
}

type SpamToggleRowProps = {
  spamCount: number;
  expanded: boolean;
  onToggle: () => void;
};
function SpamToggleRow({ spamCount, expanded, onToggle }: SpamToggleRowProps) {
  const { t } = useTranslation();
  return (
    <Box
      as="button"
      type="button"
      onClick={onToggle}
      alignItems="Center"
      gap="200"
      aria-expanded={expanded}
      style={{
        appearance: 'none',
        border: 'none',
        background: 'transparent',
        cursor: 'pointer',
        padding: `${toRem(8)} ${toRem(12)}`,
        width: '100%',
        color: color.Surface.OnContainer,
        opacity: 0.7,
        textAlign: 'left',
        font: 'inherit',
      }}
    >
      <Icon size="100" src={expanded ? Icons.ChevronTop : Icons.ChevronBottom} />
      <Text as="span" size="T200" style={{ fontWeight: 500 }}>
        {expanded
          ? t('Direct.invite_hide_spam')
          : t('Direct.invite_show_spam', { count: spamCount })}
      </Text>
    </Box>
  );
}

export function Direct() {
  const mx = useMatrixClient();
  useNavToActivePathMapper('direct');
  const scrollRef = useRef<HTMLDivElement>(null);
  const directs = useDirectRooms();
  const invites = useDirectInvites();
  const notificationPreferences = useRoomsNotificationPreferencesContext();
  const [spamExpanded, setSpamExpanded] = useState(false);
  // roomToUnreadAtom only changes on read/unread transitions and ignores own
  // events — covers incoming notifying messages but not own sends or muted
  // incoming. Kept as a subscribe-only re-render trigger.
  useAtomValue(roomToUnreadAtom);

  // Activity tick: bump on every live timeline event in any DM room so list
  // order, preview text and time labels refresh on own sends, muted incoming,
  // reactions — anything roomToUnread misses. Uses the 'Room.timeline'
  // literal (= RoomEvent.Timeline) to avoid named imports from matrix-js-sdk
  // that the project's moduleResolution flags as TS2614 (see
  // docs/known-tech-debt-lint/).
  const [, setActivityTick] = useState(0);
  useEffect(() => {
    const directsSet = new Set(directs);
    const handleTimeline = (
      _ev: unknown,
      room: { roomId: string } | undefined,
      _start: unknown,
      _removed: unknown,
      data: { liveEvent?: boolean } | undefined
    ) => {
      if (!room || !data?.liveEvent) return;
      if (!directsSet.has(room.roomId)) return;
      setActivityTick((n) => n + 1);
    };
    const emitter = mx as unknown as {
      on: (e: string, h: typeof handleTimeline) => void;
      removeListener: (e: string, h: typeof handleTimeline) => void;
    };
    emitter.on('Room.timeline', handleTimeline);
    return () => {
      emitter.removeListener('Room.timeline', handleTimeline);
    };
  }, [mx, directs]);

  const selectedRoomId = useSelectedRoom();

  const items = useMemo<ListItem[]>(() => {
    const list: ListItem[] = [];
    const cleanInvites = invites.filter((i) => !i.isSpam);
    const spamInvites = invites.filter((i) => i.isSpam);
    cleanInvites.forEach((entry) => list.push({ kind: 'invite', entry }));
    if (spamInvites.length > 0) {
      list.push({
        kind: 'spam-toggle',
        spamCount: spamInvites.length,
        expanded: spamExpanded,
      });
      if (spamExpanded) {
        spamInvites.forEach((entry) => list.push({ kind: 'spam-invite', entry }));
      }
    }
    const sortedDirects = Array.from(directs).sort(factoryRoomIdByActivity(mx));
    sortedDirects.forEach((roomId) => list.push({ kind: 'direct', roomId }));
    return list;
  }, [invites, directs, spamExpanded, mx]);

  const noRoomToDisplay = items.length === 0;

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => scrollRef.current,
    // Per-kind estimates so the initial scroll height is close to actual
    // before measureElement self-corrects: invite cards are ~140px (header +
    // sub-line + buttons + optional topic), spam toggle is ~32px, DM rows
    // ~68px. Keeps first paint stable when the panel opens with invites.
    estimateSize: (index) => {
      const item = items[index];
      if (!item) return 68;
      if (item.kind === 'invite' || item.kind === 'spam-invite') return 140;
      if (item.kind === 'spam-toggle') return 32;
      return 68;
    },
    // Stable per-item identity so the measurement cache survives item-kind
    // shifts at the same index. Without this TanStack falls back to index,
    // and a DM row at index 0 inheriting an invite card's measured height
    // (or vice versa) flashes a wrong size for one frame on every list
    // mutation that adds/removes an invite at the top.
    getItemKey: (index) => {
      const item = items[index];
      if (!item) return index;
      if (item.kind === 'invite') return `invite:${item.entry.roomId}`;
      if (item.kind === 'spam-invite') return `spam-invite:${item.entry.roomId}`;
      if (item.kind === 'spam-toggle') return 'spam-toggle';
      return `direct:${item.roomId}`;
    },
    overscan: 10,
  });

  return (
    <PageNav resizable>
      <DirectStreamHeader />
      {noRoomToDisplay ? (
        <DirectEmpty />
      ) : (
        <PageNavContent scrollRef={scrollRef}>
          <Box direction="Column" gap="300">
            <NavCategory>
              <div
                style={{
                  position: 'relative',
                  height: virtualizer.getTotalSize(),
                }}
              >
                {virtualizer.getVirtualItems().map((vItem) => {
                  const item = items[vItem.index];
                  if (!item) return null;

                  if (item.kind === 'invite' || item.kind === 'spam-invite') {
                    const { entry } = item;
                    const selected = selectedRoomId === entry.roomId;
                    return (
                      <VirtualTile
                        virtualItem={vItem}
                        key={`invite-${entry.roomId}`}
                        ref={virtualizer.measureElement}
                      >
                        <DirectInviteRow
                          room={entry.room}
                          selected={selected}
                          isSpam={item.kind === 'spam-invite'}
                        />
                      </VirtualTile>
                    );
                  }

                  if (item.kind === 'spam-toggle') {
                    return (
                      <VirtualTile
                        virtualItem={vItem}
                        key="spam-toggle"
                        ref={virtualizer.measureElement}
                      >
                        <SpamToggleRow
                          spamCount={item.spamCount}
                          expanded={item.expanded}
                          onToggle={() => setSpamExpanded((v) => !v)}
                        />
                      </VirtualTile>
                    );
                  }

                  // kind === 'direct'
                  const { roomId } = item;
                  const room = mx.getRoom(roomId);
                  if (!room) return null;
                  const selected = selectedRoomId === roomId;
                  return (
                    <VirtualTile
                      virtualItem={vItem}
                      key={`direct-${roomId}`}
                      ref={virtualizer.measureElement}
                    >
                      <DmStreamRow
                        room={room}
                        selected={selected}
                        linkPath={getDirectRoomPath(getCanonicalAliasOrRoomId(mx, roomId))}
                        notificationMode={getRoomNotificationMode(
                          notificationPreferences,
                          room.roomId
                        )}
                      />
                    </VirtualTile>
                  );
                })}
              </div>
            </NavCategory>
          </Box>
        </PageNavContent>
      )}
      <DirectNewChatRow />
      <DirectSelfRow />
    </PageNav>
  );
}
