import type { RefObject } from 'react';
import type { DataGridProps } from '../types';
import { throttleEvent } from '../../../utils';

const GRID_MAIN_CLASS = 'MuiDataGrid-main';
const GRID_VIRTUAL_SCROLLER_CLASS = 'MuiDataGrid-virtualScroller';

const CUSTOMIZED_WEBKIT_SCROLLBAR_CLASS = 'customizedWebkitScrollbar';
const INVISIBLE_WEBKIT_SCROLLBAR_CLASS = 'invisibleWebkitScrollbar';

const STICKY_HORIZONTAL_SCROLLBAR_HEIGHT = 16;

const STICKY_HORIZONTAL_SCROLLER_CLASS = 'autoHeightStickyHorizontalScroller';
const STICKY_HORIZONTAL_SCROLLER_ID = 'p81DataGrid-scrollerId';

const STICKY_PAGINATION_CONTAINER_INNER_CLASS = 'stickyPaginationContainerInner';

function createStickyScroller(instanceId: number) {
  const scrollbarWidth = Math.max(
    window.innerWidth - document.documentElement.clientWidth,
    1 // 0px height will hide StickyScroller
  );

  const outerEl = document.createElement('div');
  outerEl.className = STICKY_HORIZONTAL_SCROLLER_CLASS;
  outerEl.id = `${STICKY_HORIZONTAL_SCROLLER_ID}-${instanceId}`;
  outerEl.style.position = 'fixed';
  outerEl.style.bottom = '0px';
  outerEl.style.zIndex = '4';
  outerEl.style.minHeight = `${scrollbarWidth}px`;
  outerEl.style.overflow = 'auto hidden';

  const innerEl = document.createElement('div');
  innerEl.style.position = 'absolute';
  innerEl.style.height = '1px';

  outerEl.appendChild(innerEl);
  return outerEl;
}

function isScrollerVisible(rootEl: HTMLDivElement, headerHeight?: number, footerHeight = 0) {
  const viewportHeight = Math.max(
    document.documentElement.clientHeight || 0,
    window.innerHeight || 0
  );
  const rootRect = rootEl.getBoundingClientRect();
  return (
    rootRect.top + (headerHeight || 0) + STICKY_HORIZONTAL_SCROLLBAR_HEIGHT + footerHeight <=
      viewportHeight && rootRect.bottom + footerHeight >= viewportHeight
  );
}

function setElementVisibility(element: HTMLElement, isVisible: boolean) {
  if (isVisible) {
    element.style.opacity = '1';
    element.style.visibility = 'visible';
  } else {
    element.style.opacity = '0';
    element.style.visibility = 'hidden';
  }
}

export const useStickyHorizontalScrollerHandler = (
  apiRef: DataGridProps['apiRef'],
  stickyPaginationRef?: RefObject<HTMLElement>,
  headerHeight?: DataGridProps['headerHeight'],
  customizeScrollbars?: boolean
) => {
  const instanceId = apiRef?.current.instanceId || 0;
  let stickyScrollerEl: HTMLDivElement;
  let virtualScrollerEl: HTMLDivElement;

  const stickyPaginationEl = stickyPaginationRef?.current;
  const stickyPaginationHeight = stickyPaginationEl?.offsetHeight || 0;

  const stickyScrollerHandler = () => (stickyScrollerEl.scrollLeft = virtualScrollerEl.scrollLeft);
  const virtualScrollerHandler = () => (virtualScrollerEl.scrollLeft = stickyScrollerEl.scrollLeft);

  return (e?: Event, unmount?: boolean) => {
    const rootEl = getGridRootElement();
    const mainEl = rootEl?.querySelector(`.${GRID_MAIN_CLASS}`);
    if (!rootEl || !mainEl) return;

    if (unmount) {
      stickyScrollerEl?.remove();
      syncStickyPagination(false);
      return;
    }

    stickyScrollerEl = mainEl.querySelector(
      `#${STICKY_HORIZONTAL_SCROLLER_ID}-${instanceId}.${STICKY_HORIZONTAL_SCROLLER_CLASS}`
    ) as HTMLDivElement;

    virtualScrollerEl = mainEl.querySelector(`.${GRID_VIRTUAL_SCROLLER_CLASS}`) as HTMLDivElement;

    let isNewStickyScroller = false;
    if (!stickyScrollerEl) {
      isNewStickyScroller = true;
      stickyScrollerEl = createStickyScroller(instanceId);
      mainEl.appendChild(stickyScrollerEl);
    }
    if (!stickyScrollerEl || !virtualScrollerEl) return;

    if (customizeScrollbars) {
      customizeWebkitScrollbars(true);
      syncScrollerAndPaginationVisibility(
        isScrollerVisible(rootEl, headerHeight, stickyPaginationHeight)
      );
      syncScrollerAndDataGridWidths();
      if (isNewStickyScroller) {
        setScrollEventListeners();
      }
    } else {
      customizeWebkitScrollbars(false);
      syncScrollerAndPaginationVisibility(false);
    }
  };

  function customizeWebkitScrollbars(enable = false) {
    if (enable) {
      stickyScrollerEl.classList.add(CUSTOMIZED_WEBKIT_SCROLLBAR_CLASS);
      virtualScrollerEl.classList.add(CUSTOMIZED_WEBKIT_SCROLLBAR_CLASS);
    } else {
      stickyScrollerEl.classList.remove(CUSTOMIZED_WEBKIT_SCROLLBAR_CLASS);
      virtualScrollerEl.classList.remove(CUSTOMIZED_WEBKIT_SCROLLBAR_CLASS);
    }
  }

  function getGridRootElement() {
    return apiRef?.current?.rootElementRef?.current;
  }

  function setScrollEventListeners() {
    // sync scroll positions on init
    stickyScrollerHandler();

    // mutually synchronize positions
    virtualScrollerEl.onscroll = stickyScrollerHandler;
    stickyScrollerEl.onscroll = throttleEvent(virtualScrollerHandler);
  }

  function syncScrollerAndPaginationVisibility(isVisible = false) {
    syncScrollerVisibility(isVisible);
    syncStickyPagination(isVisible);
  }

  function syncScrollerVisibility(isVisible = false) {
    if (isVisible) {
      stickyScrollerEl && setElementVisibility(stickyScrollerEl, true);
      virtualScrollerEl.classList.add(INVISIBLE_WEBKIT_SCROLLBAR_CLASS);
    } else {
      stickyScrollerEl && setElementVisibility(stickyScrollerEl, false);
      virtualScrollerEl.classList.remove(INVISIBLE_WEBKIT_SCROLLBAR_CLASS);
    }
  }

  function syncScrollerAndDataGridWidths() {
    const stickyContentEl = stickyScrollerEl?.firstElementChild as HTMLElement;
    const virtualContentEl = virtualScrollerEl?.firstElementChild as HTMLElement;
    if (stickyContentEl && virtualContentEl) {
      stickyScrollerEl.style.width = virtualScrollerEl.style.width;
      stickyContentEl.style.width = virtualContentEl.style.width;
      if (stickyPaginationEl) {
        stickyPaginationEl.style.width = virtualScrollerEl.style.width;
      }
    }
  }

  function syncStickyPagination(isVisible = false) {
    if (!stickyPaginationEl) return;
    if (isVisible) {
      stickyScrollerEl.style.bottom = `${stickyPaginationHeight}px`;
      stickyPaginationEl.classList.add(STICKY_PAGINATION_CONTAINER_INNER_CLASS);
    } else {
      stickyPaginationEl.classList.remove(STICKY_PAGINATION_CONTAINER_INNER_CLASS);
    }
  }
};
