import { useCallback, useEffect, useMemo, useRef } from 'react';

import type { PageTypes } from '@theorchard/songwhip-events';
import type { TrackerProviderParams } from '~/src/lib/tracker/useTracker';
import type { CSSProperties, FC, ReactNode, RefObject } from 'react';
import type { PageHeaderProps } from '../PageHeader';
import type { Scroller2Props } from '../Scroller2';
import type { TransitionInOut2Api } from '../TransitionInOut2';

import useTheme from '~/src/hooks/useTheme';
import { useClickTracking } from '~/src/lib/tracker/useClickTracking';
import { TrackerProvider, useTracker } from '~/src/lib/tracker/useTracker';
import { onceAppHydrated } from '~/src/lib/utils/onceAppHydrated';
import Box from '../Box';
import Gradient from '../Gradient';
import PageHeader from '../PageHeader';
import Scroller2 from '../Scroller2';
import TransitionInOut2 from '../TransitionInOut2';

export type PageTrackerContext = TrackerProviderParams['baseContext'];

export interface PageProps extends Scroller2Props {
  renderFooter?: () => ReactNode;
  renderHeader?: () => ReactNode;
  renderHeaderContent?: () => ReactNode;
  renderHeaderLeft?: PageHeaderProps['renderLeft'];
  renderHeaderRight?: PageHeaderProps['renderRight'];
  footerHeight?: number | string;
  footerStyle?: CSSProperties;
  contentStyle?: CSSProperties;
  withSmoothScroll?: boolean;
  withGradient?: boolean;
  withHeaderGradient?: boolean;
  testId?: string;
  backHref?: string;
  withBackButton?: boolean;
  withMenuButton?: boolean;
  pageType?: PageTypes;
  trackPageView?: boolean;
  trackerContext?: PageTrackerContext;
  children?: ReactNode;
  backgroundColor?: string;
  textColor?: string;
  nodeRef?: RefObject<HTMLDivElement | null>;
}

// TODO: it would be nice to bundle <PageMetadata> inside this component
const PageInner: FC<Omit<PageProps, 'trackerContext'>> = ({
  renderFooter,
  renderHeader,
  renderHeaderContent,
  renderHeaderRight,
  renderHeaderLeft,
  footerHeight,
  footerStyle,
  contentStyle,
  testId,
  withGradient,
  withHeaderGradient = true,
  onScroll,
  children,
  backHref,
  withMenuButton,
  withBackButton,
  withScrollBar = true,
  style,
  pageType,
  trackPageView,
  backgroundColor,
  textColor,
  renderContent,
  nodeRef,
  ...scrollerProps
}) => {
  const headerContentTransitionRef = useRef<TransitionInOut2Api>(null);
  const headerGradientTransitionRef = useRef<TransitionInOut2Api>(null);
  const pageViewTrackedRef = useRef(false);
  const { trackEvent } = useTracker();
  const theme = useTheme();

  useClickTracking();

  const scrollerRenderBefore = useCallback(() => {
    if (renderHeader) return renderHeader();

    if (renderHeaderContent) {
      return (
        <PageHeader
          withGradient={withHeaderGradient}
          withBackButton={withBackButton}
          gradientApiRef={headerGradientTransitionRef}
          gradientIsVisibleInitial={false}
          renderRight={renderHeaderRight}
          renderLeft={renderHeaderLeft}
          backHref={backHref}
          withMenuButton={withMenuButton}
        >
          {/* 
            Header content without gradient has background by default
            and it does not make sense to hide content on scroll
          */}
          {withHeaderGradient ? (
            <TransitionInOut2
              apiRef={headerContentTransitionRef}
              isVisibleInitial={true}
              duration={150}
              styleFrom={{ transform: 'translateY(-50%)' }}
              style={{ willChange: 'opacity,transform' }}
            >
              {renderHeaderContent()}
            </TransitionInOut2>
          ) : (
            renderHeaderContent()
          )}
        </PageHeader>
      );
    }
  }, [renderHeader, renderHeaderContent]);

  const onScrollInternal = useCallback((params) => {
    const logo = headerContentTransitionRef.current;
    const gradient = headerGradientTransitionRef.current;
    const isScrolled = params.y > 0;

    if (isScrolled) {
      if (logo) logo.setVisible(false);
      if (gradient) gradient.setVisible(true);
    } else {
      if (logo) logo.setVisible(true);
      if (gradient) gradient.setVisible(false);
    }

    if (onScroll) onScroll(params);
  }, []);

  useEffect(() => {
    // don't track anything if the caller is not ready
    if (trackPageView !== false) {
      // prevent double tracking
      if (!pageViewTrackedRef.current) {
        // delay to avoid making network requests on critical path
        onceAppHydrated(() => {
          // wait one tick to ensure document.title is updated,
          // sometimes this can show the previous page title
          setTimeout(() => {
            trackEvent({
              type: 'page-view',
              pageType,
            });
          });
        });

        // flag as tracked
        pageViewTrackedRef.current = true;
      }
    }
  }, [trackPageView]);

  return (
    <Box
      positionRelative
      nodeRef={nodeRef}
      fullHeight
      testId={testId}
      style={{
        background: backgroundColor || theme.background,
        color: textColor || theme.textColor,
        ...style,
      }}
    >
      <Scroller2
        {...scrollerProps}
        renderContent={renderContent}
        testId="pageScroller"
        // always show a scrollbar cus ms windows
        withScrollBar={withScrollBar}
        renderBefore={scrollerRenderBefore}
        onScroll={onScrollInternal}
        style={{
          position: 'relative',
          zIndex: 0,
        }}
        contentStyle={useMemo(
          () => ({
            paddingBottom: footerHeight,
            ...contentStyle,
          }),
          [footerHeight, contentStyle]
        )}
      >
        {withGradient && (
          <Gradient
            from="rgba(255,255,255,0.1)"
            to="rgba(255,255,255,0)"
            angle={0}
            coverParent
            zIndex={-1}
            pointerEvents="none"
          />
        )}
        {children}
      </Scroller2>
      {renderFooter && (
        <Box
          tag="footer"
          height={footerHeight}
          style={{
            contain: 'strict',

            // footer content must selectively use pointer-events: all
            // to enable click targets
            pointerEvents: 'none',

            ...footerStyle,
          }}
          positionAbsolute
          left={0}
          bottom={0}
          right={0}
          zIndex={1}
        >
          {renderFooter()}
        </Box>
      )}
    </Box>
  );
};

const Page: FC<PageProps> = ({ trackerContext, pageType, ...props }) => (
  <TrackerProvider baseContext={{ pageType, ...trackerContext }}>
    <PageInner {...props} pageType={pageType} />
  </TrackerProvider>
);

export default Page;
