import {useCallback, useRef, useState} from 'react';
import {View} from 'react-native';
import {IconDownload} from '@tabler/icons-react-native';
import {ButtonSize, IconButton, Spinner} from '@components';
import {
  ProgressiveImage,
  ProgressiveImageHandle,
} from '@modules/chats/components';
import {useMediaAsset} from '@modules/chats/hooks';
import {getMessageImageTileSizing} from '@modules/chats/utils';
import {useTheme} from '@shared/theme';

import {Remaining} from '../Remaining';
import {getTileSizeStyle, ImageSize, tileStyles} from '../tileStyles';

interface Props {
  size: ImageSize;
  messageId: string;
  attachmentId: number;
  remaining?: number;
  onPress: (attachmentId: number) => void;
}

export function ImageRenderer({
  size,
  messageId,
  attachmentId,
  remaining,
  onPress,
}: Props) {
  const {imageUrl} = useMediaAsset(messageId, attachmentId);
  const {theme} = useTheme();

  const sizeStyle = getTileSizeStyle(size);
  const errorIconSize: ButtonSize = size === 'small' ? 'sm' : 'lg';

  const onItemPress = useCallback(() => {
    onPress(attachmentId);
  }, [onPress, attachmentId]);

  // ProgressiveImage auto-retries up to AUTO_RETRY_LIMIT times silently
  // before calling `onError`. Once it does, we paint the retry button.
  // Tap → call `reloadAsync` via the imperative handle (no remount) so
  // ExpoImage re-fetches in place. While a retry is in flight (auto OR
  // manual) we swap the icon for a Spinner so the user gets immediate
  // feedback.
  const progressiveRef = useRef<ProgressiveImageHandle>(null);
  const [hasError, setHasError] = useState(false);
  const [isRetrying, setIsRetrying] = useState(false);
  const onImageError = useCallback(() => {
    setIsRetrying(false);
    setHasError(true);
  }, []);
  const onImageLoad = useCallback(() => {
    setIsRetrying(false);
    setHasError(false);
  }, []);
  const onRetry = useCallback(() => {
    setIsRetrying(true);
    setHasError(false);
    progressiveRef.current?.reload().catch(() => {});
  }, []);

  // Single source of truth for tile widths — same helper drives the
  // prefetcher so warmed bytes match the render request. `size === 'big'`
  // is the 1-attachment layout; multi-grid passes any count ≥ 2 (only
  // the singular case affects the result).
  const {placeholderWidth, upgradeWidth} = getMessageImageTileSizing(
    size === 'big' ? 1 : 2,
  );

  return (
    <View style={tileStyles.container} accessible={false}>
      <ProgressiveImage
        ref={progressiveRef}
        url={imageUrl}
        smallWidth={placeholderWidth}
        largeWidth={upgradeWidth}
        style={sizeStyle}
        contentFit="cover"
        onPress={onItemPress}
        useRNGHPressable
        onError={onImageError}
        onLoad={onImageLoad}
      />
      {(hasError || isRetrying) && (
        <View
          style={[
            sizeStyle,
            tileStyles.errorContainer,
            {backgroundColor: theme.background.elements.grey},
          ]}>
          {isRetrying ? (
            <Spinner />
          ) : (
            <IconButton
              onPress={onRetry}
              variant="primary"
              size={errorIconSize}
              Icon={IconDownload}
            />
          )}
        </View>
      )}
      {!!remaining && <Remaining count={remaining} sizeStyle={sizeStyle} />}
    </View>
  );
}
