import { useEffect, useRef, type ComponentRef, type RefObject } from 'react';
import {
  Animated,
  type EmitterSubscription,
  Keyboard,
  useWindowDimensions,
} from 'react-native';
import type { HeightAnimationDriver, UseHandleKeyboardEvents } from './types.d';

/**
 * Handles keyboard pop out by adjusting sheet's layout when a `TextInput` within
 * the sheet receives focus.
 *
 * @param {boolean} keyboardHandlingEnabled Determines whether this hook will go on to handle keyboard
 * @param {number} sheetHeight Initial sheet's height before keyboard pop out
 * @param {boolean} sheetOpen Indicates whether the sheet is open or closed
 * @param {HeightAnimationDriver} heightAnimationDriver Animator function to be called with new
 * sheet height when keyboard is out so it can adjust the sheet height with animation
 * @param {RefObject<ComponentRef<typeof Animated.View> | null>} contentWrapperRef Reference to the content wrapper view
 * @return {{removeKeyboardListeners:Function;} | null} An Object with an unsubscriber function or `null`
 *  when `keyboardHandlingEnabled` is false
 */
const useHandleKeyboardEvents: UseHandleKeyboardEvents = (
  keyboardHandlingEnabled: boolean,
  sheetHeight: number,
  sheetOpen: boolean,
  heightAnimationDriver: HeightAnimationDriver,
  contentWrapperRef: RefObject<ComponentRef<typeof Animated.View> | null>,
  openDuration = 400,
  closeDuration = 200,
  containerHeight?: number
) => {
  const SCREEN_HEIGHT = useWindowDimensions().height;
  const keyboardHideSubscription = useRef<EmitterSubscription | null>(null);
  const keyboardShowSubscription = useRef<EmitterSubscription | null>(null);

  const unsubscribe = () => {
    keyboardHideSubscription.current?.remove?.();
    keyboardShowSubscription.current?.remove?.();
  };

  useEffect(() => {
    if (keyboardHandlingEnabled) {
      keyboardShowSubscription.current = Keyboard.addListener(
        'keyboardDidShow',
        ({ endCoordinates: { height: keyboardHeight } }) => {
          if (sheetOpen) {
            // Exaggeration of the actual height (24) of the autocorrect view
            // that appears atop android keyboard
            const keyboardAutoCorrectViewHeight = 50;
            contentWrapperRef.current?.measure?.(
              (
                _x: number,
                _y: number,
                _width: number,
                _height: number,
                _pageX: number,
                pageY: number
              ) => {
                const sheetYOffset = pageY; // Y offset of the sheet after keyboard is out
                const actualSheetHeight = SCREEN_HEIGHT - sheetYOffset; // new height of the sheet (after keyboard offset)
                /**
                 * We determine soft input/keyboard mode based on the difference between the new sheet height and the
                 * initial height after keyboard is opened. If there's no much difference, it's adjustPan
                 * (keyboard overlays sheet), else it's adjustResize (sheet is pushed up)
                 */
                const sheetIsOverlayed =
                  actualSheetHeight - sheetHeight <
                  keyboardAutoCorrectViewHeight;
                const remainingSpace = SCREEN_HEIGHT - keyboardHeight;

                /**
                 * this is 50% of the remaining space (SCREEN_HEIGHT - keyboardHeight) that remains atop the keybaord
                 */
                const fiftyPercent = 0.5 * remainingSpace;
                const minSheetHeight = 50;
                // allow very short sheet e.g 10 to increase to 50 and
                // very long to clamp withing availablle space;
                let newSheetHeight = Math.max(
                  minSheetHeight,
                  Math.min(sheetHeight, fiftyPercent)
                );
                if (sheetIsOverlayed) newSheetHeight += keyboardHeight;

                // Clamp to container height (or screen height fallback) to prevent overshooting
                const maxAllowedHeight = containerHeight ?? SCREEN_HEIGHT;
                if (newSheetHeight > maxAllowedHeight) {
                  newSheetHeight = maxAllowedHeight;
                }

                heightAnimationDriver(newSheetHeight, openDuration).start();
              }
            );
          }
        }
      );

      keyboardHideSubscription.current = Keyboard.addListener(
        'keyboardDidHide',
        (_) => {
          if (sheetOpen)
            heightAnimationDriver(sheetHeight, closeDuration).start();
        }
      );
      return unsubscribe;
    }
    return;
  }, [
    keyboardHandlingEnabled,
    sheetHeight,
    SCREEN_HEIGHT,
    sheetOpen,
    heightAnimationDriver,
    contentWrapperRef,
    openDuration,
    closeDuration,
    containerHeight,
  ]);

  return keyboardHandlingEnabled
    ? {
        removeKeyboardListeners: unsubscribe,
      }
    : null;
};

export default useHandleKeyboardEvents;
