import React, { useEffect, useState } from 'react';
import type { StyleProp } from 'react-native';
import { View } from 'react-native';
import FastImage, { type ImageStyle } from '@d11/react-native-fast-image';
import { isAndroid } from '../../constants/device';

interface CachedImagePlaceholderProps {
    isLoading: boolean;
    style?: StyleProp<ImageStyle>;
    Placeholder?: React.ReactElement | React.ComponentType<object>;
    showPlaceholderOnError: boolean;
    hasError: boolean;
}

const getShouldRenderPlaceholder = ({
    isLoading,
    Placeholder,
    showPlaceholderOnError,
    hasError
}: Pick<
    CachedImagePlaceholderProps,
    'isLoading' | 'Placeholder' | 'showPlaceholderOnError' | 'hasError'
>) => {
    if (!Placeholder) {
        return false;
    }
    if (!isLoading && hasError && showPlaceholderOnError) {
        return true;
    }
    return Boolean(isLoading);
};

const CachedImagePlaceholder = ({
    isLoading,
    style,
    Placeholder,
    showPlaceholderOnError,
    hasError
}: CachedImagePlaceholderProps) => {
    const shouldRender = getShouldRenderPlaceholder({
        isLoading,
        Placeholder,
        showPlaceholderOnError,
        hasError
    });

    if (!shouldRender) {
        return null;
    }

    const PlaceholderComponent = Placeholder as React.ComponentType<object>;
    const placeholder = React.isValidElement(Placeholder) ? (
        Placeholder
    ) : (
        <PlaceholderComponent />
    );

    return (
        <View testID="placeholder" style={style}>
            {placeholder}
        </View>
    );
};

interface CachedImageProps {
    uri?: string;
    style?: StyleProp<ImageStyle>;
    Placeholder?: React.ReactElement | React.ComponentType<object>;
    showPlaceholderOnError?: boolean;
    testID?: string;
}

const CachedImage = ({
    uri,
    style,
    Placeholder,
    showPlaceholderOnError = true,
    testID
}: CachedImageProps) => {
    const [isLoading, setIsLoading] = useState(false);
    const [hasError, setHasError] = useState(false);

    const handleLoadEnd = () => setIsLoading(false);
    const handleLoadStart = () => setIsLoading(true);
    const handleError = () => setHasError(true);

    // Workaround for Android: react-native-fast-image doesn't call onError for null/undefined/'' values
    useEffect(() => {
        if (isAndroid()) {
            setHasError(!uri);
        }
    }, [uri]);

    const source = uri ? { uri } : undefined;

    return (
        <>
            <CachedImagePlaceholder
                style={style}
                isLoading={isLoading || !uri}
                Placeholder={Placeholder}
                showPlaceholderOnError={showPlaceholderOnError}
                hasError={hasError}
            />
            <FastImage
                testID={testID}
                style={style}
                source={source}
                onLoadEnd={handleLoadEnd}
                onLoadStart={handleLoadStart}
                onError={handleError}
            />
        </>
    );
};

export default CachedImage;
