import React, {
  ReactNode,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import {
  Image,
  View,
  ImageProps as RNImageProps,
  Pressable,
  ImageSourcePropType,
  ViewStyle,
  StyleProp,
} from 'react-native';
import Pinchable from 'react-native-pinchable';
import {
  Image as ExpoImage,
  ImageSource as ExpoImageSource,
  ImageProps as ExpoImageProps,
  ImageStyle as ExpoImageStyle,
} from 'expo-image';
import {ImageSkeleton} from '@components/Image/components/ImageSkeleton';
import ActivityIndicator from '@components/ActivityIndicator';
import {Spinner} from '@components/_HuGo/Spinner';
import {useGetAuthHeaders} from '@hooks/useGetAuthHeaders';

import {styles} from './styles';
import {extractUriFromSource} from './utils';

enum ImageStatus {
  Loading = 'LOADING',
  Error = 'ERROR',
  Success = 'SUCCESS',
}

export interface ImageProps extends Omit<RNImageProps, 'onLoad' | 'source'> {
  source: ImageSourcePropType | ExpoImageSource;
  onPress?: () => void;
  onLongPress?: () => void;
  pinchable?: boolean;
  isLoading?: boolean;
  cached?: boolean;
  loaderType?: 'skeleton' | 'activityIndicator' | 'spinner' | 'none';
  containerStyle?: StyleProp<ViewStyle>;
  renderError?: ReactNode;
  noRenderOnError?: boolean;
  withAuthHeader?: boolean;
}

/**
 * @deprecated Use `_core/Image` instead
 */
export function CustomImage({
  source,
  style,
  onPress,
  pinchable,
  isLoading,
  containerStyle,
  renderError,
  noRenderOnError,
  onLongPress,
  cached = false,
  withAuthHeader = false,
  loaderType = 'none',
  ...props
}: ImageProps) {
  const timeoutRef = useRef<NodeJS.Timeout>(undefined);
  const [status, setStatus] = useState<ImageStatus>(ImageStatus.Loading);
  const url = useMemo(() => extractUriFromSource(source), [source]);
  const injectAuthHeader = withAuthHeader && url?.startsWith('http');
  const authHeaders = useGetAuthHeaders(injectAuthHeader);

  const handleLoad = useCallback(() => {
    timeoutRef.current && clearTimeout(timeoutRef.current);
    setStatus(ImageStatus.Success);
  }, []);

  const handleError = useCallback(() => {
    timeoutRef.current && clearTimeout(timeoutRef.current);
    setStatus(ImageStatus.Error);
  }, []);

  const ImageContainer = pinchable ? Pinchable : View;
  const showLoader =
    status === ImageStatus.Loading ||
    (withAuthHeader && !authHeaders) ||
    !!isLoading;

  const LoaderRender = {
    activityIndicator: (
      <View
        style={[styles.loader, styles.image, style as StyleProp<ViewStyle>]}>
        <ActivityIndicator />
      </View>
    ),
    skeleton: (
      <ImageSkeleton
        style={[styles.loader, styles.image, style as StyleProp<ViewStyle>]}
      />
    ),
    spinner: (
      <View
        style={[styles.loader, styles.image, style as StyleProp<ViewStyle>]}>
        <Spinner />
      </View>
    ),
    none: null,
  };

  useEffect(() => {
    timeoutRef.current = setTimeout(() => {
      setStatus(ImageStatus.Error);
    }, 10000);

    return () => {
      timeoutRef.current && clearTimeout(timeoutRef.current);
    };
  }, []);

  const sourceInfo = url
    ? {
        uri: url,
        ...(injectAuthHeader && authHeaders ? authHeaders : {}),
      }
    : source;

  return status === ImageStatus.Error && noRenderOnError ? null : (
    <Pressable
      onPress={onPress}
      onLongPress={onLongPress}
      style={containerStyle}
      disabled={!onPress && !onLongPress}>
      <ImageContainer>
        {injectAuthHeader && !authHeaders ? (
          <View style={[styles.image, style as StyleProp<ViewStyle>]} />
        ) : (
          <>
            {cached ? (
              <ExpoImage
                source={sourceInfo}
                onLoad={handleLoad}
                onError={handleError}
                style={[styles.image, style as ExpoImageStyle]}
                {...(props as ExpoImageProps)}
              />
            ) : (
              <Image
                source={sourceInfo as ImageSourcePropType}
                onLoad={handleLoad}
                onError={handleError}
                style={[styles.image, style]}
                {...props}
              />
            )}
          </>
        )}
      </ImageContainer>
      {showLoader && LoaderRender[loaderType]}
      {status === ImageStatus.Error && (
        <View
          style={[styles.error, styles.image, style as StyleProp<ViewStyle>]}>
          {renderError}
        </View>
      )}
    </Pressable>
  );
}
