import { JoinRule } from 'matrix-js-sdk';
import { AvatarFallback, AvatarImage, color } from 'folds';
import { Icon, Icons } from '../icons';
import React, { ComponentProps, ReactEventHandler, ReactNode, forwardRef, useEffect, useState } from 'react';
import { Blurhash } from 'react-blurhash';
import * as css from './RoomAvatar.css';
import { joinRuleToIconSrc } from '../../utils/room';
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 RoomAvatarProps = {
  roomId: string;
  src?: string;
  alt?: string;
  renderFallback: () => ReactNode;
};
export function RoomAvatar({ roomId, src, alt, renderFallback }: RoomAvatarProps) {
  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 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(roomId ?? ''), color: color.Surface.Container }}
        className={css.RoomAvatar}
      >
        {renderFallback()}
      </AvatarFallback>
    );
  }

  if (authenticatedSrc && loaded) {
    return (
      <AvatarImage
        className={css.RoomAvatar}
        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(roomId ?? ''),
          color: color.Surface.Container,
          position: 'absolute',
          inset: 0,
        }}
        className={css.RoomAvatar}
      >
        {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
          className={css.RoomAvatar}
          style={{ position: 'relative', zIndex: 1, opacity: loaded ? 1 : 0 }}
          src={authenticatedSrc}
          alt={alt}
          onError={() => setError(true)}
          onLoad={handleLoad}
          draggable={false}
        />
      )}
    </div>
  );
}

export const RoomIcon = forwardRef<
  SVGSVGElement,
  Omit<ComponentProps<typeof Icon>, 'src'> & {
    joinRule: JoinRule;
    space?: boolean;
  }
>(({ joinRule, space, ...props }, ref) => (
  <Icon
    src={joinRuleToIconSrc(Icons, joinRule, space || false) ?? Icons.Hash}
    {...props}
    ref={ref}
  />
));
