import React, { ReactNode, useEffect, useRef } from "react";
import FastImage, { OnLoadEvent } from "react-native-fast-image";
import { interpolate, useAnimatedStyle } from "react-native-reanimated";
import SkeletonContent from "react-native-skeleton-content-nonexpo";
import { useColors, useReanimatedValue } from "../commons";
import { Spinner, Svg, View } from "../components";
import { StyledProps } from "./Styled";

export type ImageProps = StyledProps & {
  uri?: string;
  require?: number;
  onLoaded?: (w: number, h: number) => void;
  renderError?: () => ReactNode;
  renderLoading?: () => ReactNode;

  cover?: boolean;
  isLoading?: boolean;
  background?: boolean;
  color?: string;
};

const SVG_IMAGE_DEFAULT = `<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 12.8571V13.3929C15 14.2805 14.3284 15 13.5 15H1.5C0.671562 15 0 14.2805 0 13.3929V4.82143C0 3.93382 0.671562 3.21429 1.5 3.21429H2V10.1786C2 11.6555 3.1215 12.8571 4.5 12.8571H15ZM18 10.1786V1.60714C18 0.719531 17.3284 0 16.5 0H4.5C3.67156 0 3 0.719531 3 1.60714V10.1786C3 11.0662 3.67156 11.7857 4.5 11.7857H16.5C17.3284 11.7857 18 11.0662 18 10.1786ZM8 3.21429C8 4.1019 7.32844 4.82143 6.5 4.82143C5.67156 4.82143 5 4.1019 5 3.21429C5 2.32667 5.67156 1.60714 6.5 1.60714C7.32844 1.60714 8 2.32667 8 3.21429ZM5 8.03571L6.73484 6.17695C6.88128 6.02006 7.11872 6.02006 7.26519 6.17695L8.5 7.5L12.7348 2.96267C12.8813 2.80577 13.1187 2.80577 13.2652 2.96267L16 5.89286V9.64286H5V8.03571Z" fill="#F5F5F5"/>
</svg>
`;

export const Image = (props: ImageProps) => {
  const {
    uri,
    require,
    onLoaded,
    renderError,
    renderLoading,

    cover,
    isLoading,
    background,
    color,
    size,
    width,
    height,
  } = props;

  const sourceImage = uri ? { uri } : require;

  const colors = useColors();
  const statusRef = useRef(uri ? 1 : 0);
  const statusValue = useReanimatedValue(uri ? 1 : 0); // 0: normal, 1: loading, 2: error

  const setLoadingValue = (value: number) => {
    if (statusRef.current !== value) {
      statusRef.current = value;
      statusValue.value = value;
    }
  };

  const onLoadStart = () => {
    if (uri) setLoadingValue(1);
  };

  const onLoad = (e: OnLoadEvent) => {
    onLoaded?.(e.nativeEvent.width, e.nativeEvent.height);
  };

  const onLoadEnd = () => {
    if (statusRef.current === 1) setLoadingValue(0);
  };

  const onError = () => {
    if (statusRef.current === 1) setLoadingValue(2);
  };

  const renderDefault = () => {
    if (renderError) return renderError();

    const d = size || height || width;
    const r = typeof d === "number" ? d / 4 : 20;

    return (
      <View flex1 contentCenter bgColor={colors.white}>
        <Svg size={r} xml={SVG_IMAGE_DEFAULT} />
      </View>
    );
  };

  const renderSkeleton = () => {
    if (renderLoading) return renderLoading();

    if (!cover && !background) return <Spinner flex1 />;

    return (
      <SkeletonContent
        isLoading
        layout={[{ width: "100%", height: "100%", borderRadius: 0 }]}
      />
    );
  };

  const errorStyle = useAnimatedStyle(
    () => ({
      opacity: interpolate(statusValue.value, [0, 1, 2], [0, 0, 1]),
    }),
    []
  );

  const loadingStyle = useAnimatedStyle(
    () => ({
      opacity: interpolate(statusValue.value, [0, 1, 2], [0, 1, 0]),
    }),
    []
  );

  useEffect(() => {
    onLoadStart();
  }, [uri]);

  return (
    <View
      {...props}
      hidden
      absoluteFill={background}
      width={size || width || "100%"}
      height={size || height || "100%"}
    >
      {!!sourceImage && (
        <>
          {/* error */}
          <View absoluteFill reanimatedStyle={errorStyle}>
            {renderDefault()}
          </View>

          {/* loading */}
          <View absoluteFill reanimatedStyle={loadingStyle}>
            {renderSkeleton()}
          </View>
        </>
      )}

      {/* image */}
      {!sourceImage ? (
        isLoading ? (
          renderSkeleton()
        ) : (
          renderDefault()
        )
      ) : (
        <FastImage
          key={JSON.stringify(sourceImage)}
          source={sourceImage}
          resizeMode={
            cover || background
              ? FastImage.resizeMode.cover
              : FastImage.resizeMode.contain
          }
          tintColor={color}
          style={{ flex: 1 }}
          onLoad={onLoad}
          onLoadEnd={onLoadEnd}
          onError={onError}
        />
      )}
    </View>
  );
};
