import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { Box, Text, color, config } from 'folds';
import { EventType } from 'matrix-js-sdk';
import { ReactEditor } from 'slate-react';
import { isKeyHotkey } from 'is-hotkey';
import classNames from 'classnames';
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 { RoomTombstone } from './RoomTombstone';
import { RoomInput } from './RoomInput';
import { Page } from '../../components/page';
import { useKeyDown } from '../../hooks/useKeyDown';
import { editableActiveElement } from '../../utils/dom';
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
import { useRoomCreators } from '../../hooks/useRoomCreators';
import { useRoom } from '../../hooks/useRoom';
import { useThreadDrawerOpen } from '../../hooks/useChannelsMode';
import * as css from './RoomView.css';

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;
};

export function RoomView({ eventId }: { eventId?: string }) {
  const roomInputRef = useRef<HTMLDivElement>(null);
  const roomViewRef = useRef<HTMLDivElement>(null);
  const composerWrapRef = useRef<HTMLDivElement>(null);
  // Live composer height — RoomTimeline mirrors it as `paddingBottom` so
  // the last message stays flush above the overlay.
  const [composerHeight, setComposerHeight] = useState(0);
  // Native scroll-aware composer (Android Capacitor only). RoomTimeline
  // owns the scroll element and reports direction; we translate the wrap
  // via the `data-hidden` attribute on the CSS class. paddingBottom on
  // the timeline stays unchanged so the message column doesn't reflow.
  const [composerHidden, setComposerHidden] = useState(false);

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

  const mx = useMatrixClient();

  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());

  // M2: when the thread drawer is open, the drawer hosts its own
  // composer (RoomInput) for thread replies. We must NOT mount a
  // second RoomInput here because (a) two `<Slate>` editors crash on
  // cold-load with the shared module-level initial value (slate#6016)
  // and (b) the channel composer's local-echo would still subscribe to
  // room.timeline events and visibly react to the user's thread reply.
  // Element-web uses the same single-composer-at-a-time pattern via
  // `TimelineRenderingType.Thread` context.
  const threadDrawerOpen = useThreadDrawerOpen();

  useEffect(() => {
    const el = composerWrapRef.current;
    if (!el) {
      setComposerHeight(0);
      return undefined;
    }
    setComposerHeight(el.getBoundingClientRect().height);
    const ro = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) setComposerHeight(entry.contentRect.height);
    });
    ro.observe(el);
    return () => ro.disconnect();
  }, [threadDrawerOpen, tombstoneEvent, canMessage]);

  // Reset hidden state before paint when the room or drawer changes, so a
  // composer-hidden tail from the previous surface can't flash through the
  // 220ms transition on remount.
  useLayoutEffect(() => {
    setComposerHidden(false);
  }, [roomId, threadDrawerOpen]);

  useKeyDown(
    window,
    useCallback(
      (evt) => {
        if (editableActiveElement()) return;
        const portalContainer = document.getElementById('portalContainer');
        if (portalContainer && portalContainer.children.length > 0) {
          return;
        }
        // Drawer-active: this RoomInput is unmounted; ReactEditor.focus
        // on it silently no-ops, but the user's keystroke would bypass
        // the drawer composer instead of routing there. Skip the auto-
        // focus so the keystroke falls through to whatever has focus
        // (typically the drawer composer's CustomEditor when open).
        if (threadDrawerOpen) return;
        if (shouldFocusMessageField(evt) || isKeyHotkey('mod+v', evt)) {
          ReactEditor.focus(editor);
        }
      },
      [editor, threadDrawerOpen]
    )
  );

  // Composer renders as a true overlay (absolute, bottom-stuck) above the
  // timeline so messages can scroll behind it — WhatsApp / Telegram pattern.
  // RoomTimeline takes `bottomOverlayHeight` as a prop and both (a) reserves
  // bottom scroll-padding for the overlay so the latest message sits flush
  // above it at rest and (b) re-anchors scrollTop in a layout effect when
  // the height transitions (e.g. composer measures async after mount, multi-
  // line typing, reply-preview opens).
  return (
    <Page
      ref={roomViewRef}
      style={{ backgroundColor: color.SurfaceVariant.Container, position: 'relative' }}
    >
      <Box grow="Yes" direction="Column">
        <RoomTimeline
          key={roomId}
          room={room}
          eventId={eventId}
          roomInputRef={roomInputRef}
          editor={editor}
          bottomOverlayHeight={composerHeight}
          onComposerHiddenChange={setComposerHidden}
        />
      </Box>
      {!threadDrawerOpen && (
        <div
          ref={composerWrapRef}
          // zIndex (in css.ComposerOverlay) must beat the Stream-rail dot
          // halos (zIndex: 2 in layout.css.ts) — shared stacking context
          // with the Page. `data-hidden` toggles the slide/fade.
          // `onFocusCapture` covers programmatic focus while the wrap is
          // off-screen (e.g. `ReactEditor.focus(editor)` from useKeyDown's
          // auto-focus when the user starts typing).
          className={css.ComposerOverlay}
          data-hidden={composerHidden ? 'true' : 'false'}
          onFocusCapture={() => setComposerHidden(false)}
        >
          <div
            // Every room class fits its composer to the same centred ~960px band
            // as the timeline (BubbleTimelineBand) and the AI-bot chat, so on wide
            // web the input lines up with the message column. The band owns its own
            // responsive horizontal padding, so no inline padding here.
            className={classNames(css.ChatComposer, css.ComposerBubbleBand)}
          >
            {tombstoneEvent ? (
              <RoomTombstone
                roomId={roomId}
                body={tombstoneEvent.getContent().body}
                replacementRoomId={tombstoneEvent.getContent().replacement_room}
              />
            ) : (
              <>
                {canMessage && (
                  <RoomInput
                    room={room}
                    editor={editor}
                    roomId={roomId}
                    fileDropContainerRef={roomViewRef}
                    ref={roomInputRef}
                  />
                )}
                {!canMessage && (
                  <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>
                )}
              </>
            )}
          </div>
        </div>
      )}
    </Page>
  );
}
