import React, { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { Box, Text, config } from 'folds';
import { EventType, Room } from 'matrix-js-sdk';
import { ReactEditor } from 'slate-react';
import { isKeyHotkey } from 'is-hotkey';
import { useAtomValue, useSetAtom, getDefaultStore } from 'jotai';
import { Editor } from 'slate';
import { useStateEvent } from '../../hooks/useStateEvent';
import { StateEvent } from '../../../types/matrix/room';
import { usePowerLevelsContext } from '../../hooks/usePowerLevels';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { useEditor } from '../../components/editor';
import { RoomInputPlaceholder } from './RoomInputPlaceholder';
import { RoomTimeline } from './RoomTimeline';
import { RoomViewTyping } from './RoomViewTyping';
import { RoomTombstone } from './RoomTombstone';
import { RoomInput } from './RoomInput';
import { RoomViewFollowing, RoomViewFollowingPlaceholder } from './RoomViewFollowing';
import { Page } from '../../components/page';
import { RoomViewHeader } from './RoomViewHeader';
import { useKeyDown } from '../../hooks/useKeyDown';
import { editableActiveElement } from '../../utils/dom';
import { settingsAtom } from '../../state/settings';
import { useSetting } from '../../state/hooks/settings';
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
import { useRoomCreators } from '../../hooks/useRoomCreators';
import { activeThreadIdAtomFamily } from '../../state/activeThread';
import { pendingRoomNavigationAtom } from '../../state/pendingRoomNavigation';
import { activeRoomIdAtom } from '../../state/activeRoom';
import { ThreadView } from './ThreadView';

const FN_KEYS_REGEX = /^F\d+$/;
const shouldFocusMessageField = (evt: KeyboardEvent): boolean => {
  const { code } = evt;
  if (evt.metaKey || evt.altKey || evt.ctrlKey) {
    return false;
  }

  if (FN_KEYS_REGEX.test(code)) return false;

  if (
    code.startsWith('OS') ||
    code.startsWith('Meta') ||
    code.startsWith('Shift') ||
    code.startsWith('Alt') ||
    code.startsWith('Control') ||
    code.startsWith('Arrow') ||
    code.startsWith('Page') ||
    code.startsWith('End') ||
    code.startsWith('Home') ||
    code === 'Tab' ||
    code === 'Space' ||
    code === 'Enter' ||
    code === 'NumLock' ||
    code === 'ScrollLock'
  ) {
    return false;
  }

  return true;
};

/**
 * Slate must not stay mounted in hidden keep-alive rooms — multiple Editable
 * trees crash with findPath. Timeline stays warm; only the composer remounts.
 */
function RoomComposerSlot({
  room,
  roomId,
  editor,
  canMessage,
  active,
  roomViewRef,
  roomInputRef,
}: {
  room: Room;
  roomId: string;
  editor: Editor;
  canMessage: boolean;
  active: boolean;
  roomViewRef: React.RefObject<HTMLDivElement | null>;
  roomInputRef: React.RefObject<HTMLDivElement | null>;
}) {
  const [mountComposer, setMountComposer] = useState(active);

  useLayoutEffect(() => {
    if (!active) {
      setMountComposer(false);
      try {
        ReactEditor.blur(editor);
      } catch {
        // Editor may already be disconnected.
      }
      return undefined;
    }
    const raf = window.requestAnimationFrame(() => setMountComposer(true));
    return () => window.cancelAnimationFrame(raf);
  }, [active, editor]);

  useEffect(() => {
    if (active) return undefined;
    try {
      ReactEditor.blur(editor);
    } catch {
      // ignore
    }
    return undefined;
  }, [active, editor]);

  if (!canMessage) {
    return (
      <RoomInputPlaceholder
        style={{ padding: config.space.S200 }}
        alignItems="Center"
        justifyContent="Center"
      >
        <Text align="Center">You do not have permission to post in this room</Text>
      </RoomInputPlaceholder>
    );
  }

  // Gate on `active` in render (not only state) so Slate unmounts the same
  // commit the slot hides — waiting for useLayoutEffect is one frame too late.
  if (active && mountComposer) {
    return (
      <RoomInput
        room={room}
        editor={editor}
        roomId={roomId}
        fileDropContainerRef={roomViewRef}
        ref={roomInputRef}
      />
    );
  }

  return (
    <div ref={roomInputRef}>
      <RoomInputPlaceholder style={{ padding: config.space.S200 }} />
    </div>
  );
}

export const RoomView = memo(function RoomView({
  room,
  eventId,
  pending = false,
  active = true,
}: {
  room: Room;
  eventId?: string;
  /** Optimistic shell only — no live timeline (used for press→navigate gap). */
  pending?: boolean;
  /** False for hidden keep-alive slots — unmounts Slate only. */
  active?: boolean;
}) {
  const roomInputRef = useRef<HTMLDivElement>(null);
  const roomViewRef = useRef<HTMLDivElement>(null);

  const [hideActivity] = useSetting(settingsAtom, 'hideActivity');

  const { roomId } = room;
  const editor = useEditor();

  const mx = useMatrixClient();
  const setPendingRoomId = useSetAtom(pendingRoomNavigationAtom);

  const tombstoneEvent = useStateEvent(room, StateEvent.RoomTombstone);
  const powerLevels = usePowerLevelsContext();
  const creators = useRoomCreators(room);

  const permissions = useRoomPermissions(creators, powerLevels);
  const canMessage = permissions.event(EventType.RoomMessage, mx.getSafeUserId());

  const activeThreadId = useAtomValue(activeThreadIdAtomFamily(roomId));
  const showThread = !pending && !!activeThreadId;
  const composerActive = active && !pending;

  useLayoutEffect(() => {
    if (!composerActive) return;
    setPendingRoomId((id) => (id === roomId ? null : id));
  }, [composerActive, roomId, setPendingRoomId]);

  useKeyDown(
    window,
    useCallback(
      (evt) => {
        if (!composerActive) return;
        if (getDefaultStore().get(activeRoomIdAtom) !== roomId) return;
        if (editableActiveElement()) return;
        const portalContainer = document.getElementById('portalContainer');
        if (portalContainer && portalContainer.children.length > 0) {
          return;
        }
        if (shouldFocusMessageField(evt)) {
          evt.preventDefault();
          try {
            ReactEditor.focus(editor);
          } catch {
            return;
          }
          if (evt.key.length === 1) {
            editor.insertText(evt.key);
          }
        } else if (isKeyHotkey('mod+v', evt)) {
          try {
            ReactEditor.focus(editor);
          } catch {
            // Composer may not be mounted yet.
          }
        }
      },
      [composerActive, editor, roomId]
    )
  );

  return (
    <Page ref={roomViewRef}>
      <RoomViewHeader />
      {showThread ? (
        <ThreadView room={room} threadRootId={activeThreadId!} />
      ) : (
        <>
          <Box grow="Yes" direction="Column" style={{ position: 'relative', minHeight: 0 }}>
            {!pending && (
              <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
                <RoomTimeline
                  key={roomId}
                  room={room}
                  eventId={eventId}
                  roomInputRef={roomInputRef}
                  editor={editor}
                />
              </div>
            )}
            <RoomViewTyping room={room} />
          </Box>
          <Box shrink="No" direction="Column">
            <div style={{ padding: `0 ${config.space.S400}` }}>
              {tombstoneEvent ? (
                <RoomTombstone
                  roomId={roomId}
                  body={tombstoneEvent.getContent().body}
                  replacementRoomId={tombstoneEvent.getContent().replacement_room}
                />
              ) : (
                <RoomComposerSlot
                  room={room}
                  roomId={roomId}
                  editor={editor}
                  canMessage={canMessage}
                  active={composerActive}
                  roomViewRef={roomViewRef}
                  roomInputRef={roomInputRef}
                />
              )}
            </div>
            {hideActivity ? <RoomViewFollowingPlaceholder /> : <RoomViewFollowing room={room} />}
          </Box>
        </>
      )}
    </Page>
  );
});
