import { AvatarFallback, AvatarImage, color } from 'folds';
import React, { ReactEventHandler, ReactNode, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';
import { Blurhash } from 'react-blurhash';
import * as css from './UserAvatar.css';
import colorMXID from '../../../util/colorMXID';
import { useAuthenticatedMediaUrl } from '../../hooks/useAuthenticatedMediaUrl';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { cacheAvatar, isAvatarCached, pruneAvatarCache } from '../../utils/avatarCache';
import {
  getCachedAuthenticatedMediaUrl,
  isAuthenticatedMediaUrl,
} from '../../utils/authenticatedMediaCache';
import {
  getMediaBlurHash,
  getMediaDimensions,
  rememberMediaBlurHash,
} from '../../state/mediaDimensionCache';

type UserAvatarProps = {
  className?: string;
  userId: string;
  src?: string;
  alt?: string;
  renderFallback: () => ReactNode;
};
export function UserAvatar({ className, userId, src, alt, renderFallback }: UserAvatarProps) {
  const [error, setError] = useState(false);
  const useAuthentication = useMediaAuthentication();
  const authenticatedSrc = useAuthenticatedMediaUrl(src, useAuthentication);
  const blurHash = getMediaBlurHash(src ?? '');
  const dims = getMediaDimensions(src ?? '');
  const blobCached =
    !!src &&
    (!useAuthentication ||
      !isAuthenticatedMediaUrl(src) ||
      !!getCachedAuthenticatedMediaUrl(src));

  const [loaded, setLoaded] = useState(() => isAvatarCached(src) || blobCached);

  useEffect(() => {
    if (
      authenticatedSrc &&
      (isAvatarCached(src) ||
        (src && (!useAuthentication || !isAuthenticatedMediaUrl(src) || getCachedAuthenticatedMediaUrl(src))))
    ) {
      setLoaded(true);
    }
  }, [authenticatedSrc, src, useAuthentication]);

  const imgRef = useRef<HTMLImageElement>(null);

  useEffect(() => {
    if (!loaded && imgRef.current?.complete && imgRef.current?.naturalWidth > 0) {
      setLoaded(true);
      cacheAvatar(src);
    }
  }, [authenticatedSrc, loaded, src]);

  const handleLoad: ReactEventHandler<HTMLImageElement> = (evt) => {
    evt.currentTarget.setAttribute('data-image-loaded', 'true');
    setLoaded(true);
    cacheAvatar(src);
    pruneAvatarCache();
    rememberMediaBlurHash(src ?? '', evt.currentTarget);
  };

  if (!src || error) {
    return (
      <AvatarFallback
        style={{ backgroundColor: colorMXID(userId), color: color.Surface.Container }}
        className={classNames(css.UserAvatar, className)}
      >
        {renderFallback()}
      </AvatarFallback>
    );
  }

  if (authenticatedSrc && loaded) {
    return (
      <AvatarImage
        className={classNames(css.UserAvatar, className)}
        src={authenticatedSrc}
        alt={alt}
        onError={() => setError(true)}
        onLoad={handleLoad}
        draggable={false}
        data-image-loaded="true"
      />
    );
  }

  const blurResolutionX = 32;
  const blurResolutionY =
    dims?.w && dims?.h && dims.w > 0
      ? Math.max(1, Math.round(blurResolutionX * (dims.h / dims.w)))
      : 32;

  return (
    <div style={{ position: 'relative', width: '100%', height: '100%' }}>
      <AvatarFallback
        style={{
          backgroundColor: colorMXID(userId),
          color: color.Surface.Container,
          position: 'absolute',
          inset: 0,
        }}
        className={classNames(css.UserAvatar, className)}
      >
        {renderFallback()}
      </AvatarFallback>
      {blurHash && (
        <div
          style={{
            position: 'absolute',
            inset: 0,
            overflow: 'hidden',
            borderRadius: 'inherit',
            filter: 'blur(6px)',
            transform: 'scale(1.08)',
          }}
        >
          <Blurhash
            hash={blurHash}
            width="100%"
            height="100%"
            resolutionX={blurResolutionX}
            resolutionY={blurResolutionY}
            punch={1.1}
            style={{ display: 'block' }}
          />
        </div>
      )}
      {authenticatedSrc && (
        <AvatarImage
          ref={imgRef}
          className={classNames(css.UserAvatar, className)}
          style={{ position: 'relative', zIndex: 1, opacity: loaded ? 1 : 0 }}
          src={authenticatedSrc}
          alt={alt}
          onError={() => setError(true)}
          onLoad={handleLoad}
          draggable={false}
        />
      )}
    </div>
  );
}
