import { ActionIcon, Box, Button, Flex, ScrollArea } from "@mantine/core";
import { PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState } from "react";
import useAppShellHeight from "@/hooks/use-appshell-height";
import { ArrowClockwiseIcon, SpinnerIcon } from "@phosphor-icons/react";
import { useQueryClient } from "@tanstack/react-query";
import useRouterConfig from "../hooks/use-router-config";
import { useLocation } from "@tanstack/react-router";

const THRESHOLD = 80;

interface PullableProps extends PropsWithChildren {
  scrollPosition: { x: number, y: number };
  onScrollPositionChange: (position: { x: number, y: number }) => void;
}

/**
 * Pullable is a component that allows the user to pull down to refresh the page
 */
const Pullable: React.FC<PullableProps> = ({ children, scrollPosition, onScrollPositionChange }) => {
  const height = useAppShellHeight();
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [scrolling, setScrolling] = useState(false);
  const { refresh } = useRouterConfig();
  const queryClient = useQueryClient();
  const location = useLocation();
  const scrollAreaRef = useRef<HTMLDivElement>(null);

  const scrollY = useMemo(() => scrollPosition.y < 0 && scrolling ? Math.abs(scrollPosition.y) : 0, [scrollPosition.y, scrolling]);

  const onTrigger = useCallback(async () => {
    setIsRefreshing(true);
    if (refresh.length > 0) {
      // TODO: Remove this after testing - or does the delay help ux?
      await new Promise(resolve => setTimeout(resolve, 1000));
      refresh.forEach(async (queryKey) => {
        const keyArray = Array.isArray(queryKey) ? queryKey : [queryKey];
        await queryClient.refetchQueries({ queryKey: keyArray, exact: true });
      });
    }
    setIsRefreshing(false);
  }, [refresh]);

  useEffect(() => {
    if (!isRefreshing && scrollY > THRESHOLD) {
      onTrigger();
    }
  }, [scrollY, isRefreshing, onTrigger]);

  const iconOpacity = useMemo(() => {
    if (isRefreshing) return 1;
    if (refresh.length === 0) return 0;
    const clampedValue = Math.max(5, Math.min(THRESHOLD, scrollY));

    const min = 5;
    const max = THRESHOLD;
    const range = max - min;

    return (clampedValue - min) / range;
  }, [scrollY, isRefreshing])

  useEffect(() => {
    const scrollWrapper = document.getElementById('scroll-wrapper');
    if (scrollWrapper) {
      scrollWrapper.addEventListener('touchstart', () => {
        setScrolling(true);
      });
      scrollWrapper.addEventListener('touchend', () => {
        setScrolling(false);
      });
    }
  }, []);

  useEffect(() => {
		if (typeof window === 'undefined') return;

		const ac = new AbortController();
		const options = {
			passive: true,
			signal: ac.signal
		};

		window.addEventListener('touchstart', () => setScrolling(true), options);
		window.addEventListener('touchend', () => setScrolling(false), options);

		return () => void ac.abort();
	}, []);

  useEffect(() => {
    const timeoutId = setTimeout(() => {
      if (scrollAreaRef.current) {
        const viewport = scrollAreaRef.current.querySelector('.mantine-ScrollArea-viewport') as HTMLElement;
        if (viewport) {
          viewport.scrollTop = 0;
          viewport.scrollLeft = 0;
        }
      }
      onScrollPositionChange({ x: 0, y: 0 });
    }, 10);

    return () => clearTimeout(timeoutId);
  }, [location.pathname, onScrollPositionChange]);


  return (
    <>
      <Flex
        pos='absolute'
        justify='center'
        align='center'
        w='100%'
        display={scrollY > 20 || isRefreshing ? 'flex' : 'none'}
        opacity={iconOpacity}
        style={{ zIndex: 10 }}
      >
        <SpinnerIcon
          weight="bold"
          size={iconOpacity * 28}
          color='var(--mantine-color-dimmed)'
          style={{
            marginTop: 8,
            transform: iconOpacity === 1 ? undefined : `rotate(${iconOpacity * 360}deg)`,
            animation: iconOpacity === 1 ? 'spin 1s linear infinite' : undefined,
          }}
        />
      </Flex>
      <ScrollArea
        ref={scrollAreaRef}
        id='scroll-wrapper'
        onScrollPositionChange={onScrollPositionChange}
        type='never' mah='100%' h='100%'
        pt={(scrolling || scrollY > 40) || !isRefreshing ? 0 : 40 - scrollY}
      >
        <Box pt='1rem'pb='0.285rem' mih={height} style={{ boxSizing: 'content-box' }}>
          {children}
        </Box>
      </ScrollArea>
    </>
  )
}

export default Pullable;
