import React, { useCallback, useEffect, useRef } from 'react';
import {
  Box,
  Header,
  Icon,
  IconButton,
  Icons,
  Spinner,
  Text,
  Tooltip,
  TooltipProvider,
} from 'folds';
import { Room, RoomEvent, ThreadEvent } from 'matrix-js-sdk';
import { isKeyHotkey } from 'is-hotkey';
import classNames from 'classnames';
import * as css from './ThreadPanel.css';
import { ContainerColor } from '../../../styles/ContainerColor.css';
import { ThreadTimeline } from './ThreadTimeline';
import { markThreadAsRead, useThreadInstance } from './useThread';
import { useMatrixClient } from '../../../hooks/useMatrixClient';
import { useEditor } from '../../../components/editor';
import { useKeyDown } from '../../../hooks/useKeyDown';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
import { RoomInput } from '../RoomInput';
import {
  getThreadNotificationModeIcon,
  ThreadNotificationModeSwitcher,
} from '../../../components/ThreadNotificationModeSwitcher';
import { useThreadNotificationMode } from '../../../hooks/useThreadNotifications';
import { ThreadNotificationMode } from '../../../utils/threadNotifications';

type ThreadPanelHeaderProps = {
  room: Room;
  threadId: string;
  requestClose: () => void;
};
function ThreadPanelHeader({ room, threadId, requestClose }: ThreadPanelHeaderProps) {
  const mode = useThreadNotificationMode(room.roomId, threadId);

  return (
    <Header className={css.ThreadPanelHeader} variant="Background" size="600">
      <Box grow="Yes" alignItems="Center" gap="200">
        <Box grow="Yes" direction="Column">
          <Text size="H5" truncate>
            Thread
          </Text>
          <Text size="T200" truncate style={{ opacity: 0.65 }}>
            {room.name}
          </Text>
        </Box>
        <Box shrink="No" alignItems="Center" gap="100">
          <ThreadNotificationModeSwitcher roomId={room.roomId} threadId={threadId} value={mode}>
            {(handleOpen, opened) => (
              <TooltipProvider
                position="Bottom"
                align="End"
                offset={4}
                tooltip={
                  <Tooltip>
                    <Text>Notifications</Text>
                  </Tooltip>
                }
              >
                {(triggerRef) => (
                  <IconButton
                    ref={triggerRef}
                    variant="Background"
                    aria-label="Thread notifications"
                    aria-pressed={opened}
                    onClick={handleOpen}
                  >
                    <Icon
                      src={getThreadNotificationModeIcon(mode)}
                      filled={mode !== ThreadNotificationMode.Default}
                    />
                  </IconButton>
                )}
              </TooltipProvider>
            )}
          </ThreadNotificationModeSwitcher>
          <TooltipProvider
            position="Bottom"
            align="End"
            offset={4}
            tooltip={
              <Tooltip>
                <Text>Close</Text>
              </Tooltip>
            }
          >
            {(triggerRef) => (
              <IconButton
                ref={triggerRef}
                variant="Background"
                aria-label="Close thread"
                onClick={requestClose}
              >
                <Icon src={Icons.Cross} />
              </IconButton>
            )}
          </TooltipProvider>
        </Box>
      </Box>
    </Header>
  );
}

export type ThreadPanelProps = {
  room: Room;
  threadId: string;
  requestClose: () => void;
};
export function ThreadPanel({ room, threadId, requestClose }: ThreadPanelProps) {
  const mx = useMatrixClient();
  const editor = useEditor();
  const thread = useThreadInstance(room, threadId);
  const [privateReadReceipts] = useSetting(settingsAtom, 'privateReadReceipts');
  const fileDropContainerRef = useRef<HTMLDivElement>(null) as React.RefObject<HTMLDivElement>;

  useKeyDown(
    window,
    useCallback(
      (evt) => {
        if (isKeyHotkey('escape', evt)) {
          // The composer preventDefaults Escape when it consumes it (dismissing
          // autocomplete / clearing a reply draft). Don't close the panel in
          // that case — only when Escape wasn't already handled.
          if (evt.defaultPrevented) return;
          evt.preventDefault();
          evt.stopPropagation();
          requestClose();
        }
      },
      [requestClose],
    ),
  );

  // Mark the thread read when the panel is open and on each new thread event.
  // Deduped on the latest event id: RoomEvent.Timeline re-emits per event during
  // backfill and for every edit/reaction, and sendReadReceipt POSTs
  // unconditionally — without the guard, opening a thread with N replies would
  // fire up to N receipt requests at the same event.
  const lastReadEventIdRef = useRef<string | undefined>(undefined);
  useEffect(() => {
    lastReadEventIdRef.current = undefined;
    if (!thread) return undefined;
    const markRead = () => {
      const events = thread.liveTimeline.getEvents();
      let latestId: string | undefined;
      for (let i = events.length - 1; i >= 0; i -= 1) {
        const evt = events[i];
        if (evt && !evt.isSending()) {
          latestId = evt.getId() ?? undefined;
          break;
        }
      }
      if (!latestId || latestId === lastReadEventIdRef.current) return;
      lastReadEventIdRef.current = latestId;
      markThreadAsRead(mx, thread, privateReadReceipts).catch(() => {
        // Allow a retry on the next event if the receipt POST failed.
        if (lastReadEventIdRef.current === latestId) {
          lastReadEventIdRef.current = undefined;
        }
      });
    };
    markRead();
    thread.on(ThreadEvent.NewReply, markRead);
    thread.on(RoomEvent.Timeline, markRead);
    return () => {
      thread.off(ThreadEvent.NewReply, markRead);
      thread.off(RoomEvent.Timeline, markRead);
    };
  }, [mx, thread, privateReadReceipts]);

  return (
    <Box
      className={classNames(css.ThreadPanel, ContainerColor({ variant: 'Background' }))}
      shrink="No"
      direction="Column"
    >
      <ThreadPanelHeader room={room} threadId={threadId} requestClose={requestClose} />
      {!thread ? (
        <Box grow="Yes" alignItems="Center" justifyContent="Center" gap="200">
          <Spinner size="400" variant="Secondary" />
          <Text size="T300">Loading thread…</Text>
        </Box>
      ) : (
        <>
          <Box grow="Yes" className={css.ThreadPanelContent} direction="Column">
            <ThreadTimeline room={room} thread={thread} editor={editor} />
          </Box>
          <Box className={css.ThreadPanelInput} shrink="No" direction="Column">
            <RoomInput
              room={room}
              roomId={room.roomId}
              threadRootId={threadId}
              editor={editor}
              editableName="ThreadInput"
              fileDropContainerRef={fileDropContainerRef}
            />
          </Box>
        </>
      )}
    </Box>
  );
}
