import { memo, type ReactNode, useEffect, useRef } from 'react';

import Backdrop from '@material-hu/mui/Backdrop';
import Portal from '@material-hu/mui/Portal';
import Slide from '@material-hu/mui/Slide';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';
import TrapFocus from '@material-hu/mui/Unstable_TrapFocus';

import { NAVBAR_HEIGHT } from 'src/utils/sidebar';

import { NOTIFICATION_PANEL_WIDTH } from '../../constants';
import { usePanelMountState } from '../usePanelMountState';

type NotificationSidePanelProps = {
  open: boolean;
  onClose: () => void;
  onOpen?: () => void;
  onAfterOpen?: () => void;
  onAfterClose?: () => void;
  header: ReactNode;
  body: ReactNode;
  width?: number | string;
  safeAreaTop?: number | string;
  disableBackdropClose?: boolean;
};

const toCssSize = (value: number | string) =>
  typeof value === 'number' ? `${value}px` : value;

const PanelHeader = memo(({ children }: { children: ReactNode }) => (
  <Stack sx={{ flexShrink: 0 }}>{children}</Stack>
));

const PanelBody = memo(({ children }: { children: ReactNode }) => (
  <Stack sx={{ flexGrow: 1, overflowY: 'auto', overflowX: 'hidden' }}>
    {children}
  </Stack>
));

const NotificationSidePanel = ({
  open,
  onClose,
  header,
  body,
  width = NOTIFICATION_PANEL_WIDTH,
  safeAreaTop = NAVBAR_HEIGHT,
  disableBackdropClose = false,
  onOpen,
  onAfterOpen,
  onAfterClose,
}: NotificationSidePanelProps) => {
  const theme = useTheme();
  const [isMounted, handleExited] = usePanelMountState(open);
  const prevOpenRef = useRef(open);
  const prevOpenForFocusRef = useRef(open);
  const lastActiveElementRef = useRef<HTMLElement | null>(null);

  // Notify open/close callbacks and keep ref in sync when `open` changes.
  useEffect(() => {
    const notifyOpenCloseCallbacks = () => {
      if (!prevOpenRef.current && open) {
        onOpen?.();
        onAfterOpen?.();
      }
      if (prevOpenRef.current && !open) {
        onAfterClose?.();
      }
      prevOpenRef.current = open;
    };
    notifyOpenCloseCallbacks();
  }, [open, onOpen, onAfterOpen, onAfterClose]);

  // When panel is open: capture focus target and listen for Escape to close.
  useEffect(() => {
    if (!open) return;
    lastActiveElementRef.current =
      (document.activeElement as HTMLElement | null) || null;
    const handleEscapeKey = (event: KeyboardEvent) => {
      if (event.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', handleEscapeKey);
    return () => window.removeEventListener('keydown', handleEscapeKey);
  }, [open, onClose]);

  // When panel closes (transition open true → false): restore focus once.
  useEffect(() => {
    const wasOpen = prevOpenForFocusRef.current;
    prevOpenForFocusRef.current = open;
    if (wasOpen && !open) {
      const el = lastActiveElementRef.current;
      if (el?.focus) el.focus();
    }
  }, [open]);

  const topOffset = toCssSize(safeAreaTop);
  const panelHeight = `calc(100% - ${topOffset})`;
  const panelWidth = toCssSize(width);

  if (!open && !isMounted) {
    return null;
  }

  const transitionDuration = {
    enter: theme.transitions.duration.enteringScreen,
    exit: theme.transitions.duration.leavingScreen,
  };

  return (
    <Portal>
      <Backdrop
        open={open}
        onClick={() => {
          if (!disableBackdropClose) {
            onClose();
          }
        }}
        transitionDuration={transitionDuration}
        sx={{
          position: 'fixed',
          top: topOffset,
          left: 0,
          width: '100%',
          height: panelHeight,
          zIndex: 1300,
          backgroundColor: 'rgba(0, 0, 0, 0.3)',
        }}
      />
      <TrapFocus
        open={open}
        disableAutoFocus
        disableRestoreFocus
      >
        <Slide
          direction="left"
          in={open}
          timeout={transitionDuration}
          mountOnEnter
          unmountOnExit
          onExited={handleExited}
        >
          <Stack
            role="dialog"
            aria-modal={open ? 'true' : undefined}
            tabIndex={-1}
            sx={{
              position: 'fixed',
              top: topOffset,
              right: 0,
              height: panelHeight,
              width: panelWidth,
              zIndex: 1301,
              display: 'flex',
              flexDirection: 'column',
              backgroundColor: 'background.paper',
              borderRadius: 0,
              overflow: 'hidden',
            }}
          >
            <PanelHeader>{header}</PanelHeader>
            <PanelBody>{body}</PanelBody>
          </Stack>
        </Slide>
      </TrapFocus>
    </Portal>
  );
};

export default NotificationSidePanel;
