/**
 * Copied from chakra-ui, license MIT
 * Accessed 2021-12-26, commit September 9th, 2021
 * Changes: Heavily modified to use use as: or PropsOf, add displayNames
 * See: https://github.com/chakra-ui/chakra-ui/blob/a1ca91b/packages/image/src/image.tsx
 */
import React from "react";

import { useImage, UseImageProps } from "../hooks/useImage";
import { omit } from "../utils/object";

interface NativeImageOptions {
  /**
   * The native HTML `width` attribute to the passed to the `img`
   */
  htmlWidth?: string | number;
  /**
   * The native HTML `height` attribute to the passed to the `img`
   */
  htmlHeight?: string | number;
}

interface NativeImageProps
  extends React.HTMLProps<HTMLImageElement>,
    NativeImageOptions {}

const NativeImage = React.forwardRef<HTMLImageElement, NativeImageProps>(
  (props: NativeImageProps, ref: React.Ref<HTMLImageElement>) => {
    const { htmlWidth, htmlHeight, alt, ...rest } = props;

    // @ts-ignore
    return (
      <img
        width={htmlWidth}
        height={htmlHeight}
        ref={ref}
        alt={alt}
        {...rest}
      />
    );
  },
);
NativeImage.displayName = "NativeImage";

interface ImageOptions extends NativeImageOptions {
  /**
   * Fallback image `src` to show if image is loading or image fails.
   *
   * Note 🚨: We recommend you use a local image
   */
  fallbackSrc?: string;
  /**
   * Fallback element to show if image is loading or image fails.
   * @type React.ReactElement
   */
  fallback?: React.ReactElement;
  /**
   * Defines loading strategy
   */
  loading?: "eager" | "lazy";
  /**
   * How the image to fit within its bounds.
   * It maps to css `object-fit` property.
   */
  fit?: React.CSSProperties["objectFit"];
  /**
   * How to align the image within its bounds.
   * It maps to css `object-position` property.
   */
  align?: React.CSSProperties["objectPosition"];
  /**
   * If `true`, opt out of the `fallbackSrc` logic and use as `img`
   */
  ignoreFallback?: boolean;
}

export interface ImageProps
  extends UseImageProps,
    Omit<React.HTMLProps<HTMLImageElement>, keyof UseImageProps>,
    ImageOptions {}

/**
 * React component that renders an image with support
 * for fallbacks
 *
 * @see Docs https://chakra-ui.com/image
 */
export const Image = React.forwardRef<HTMLImageElement, ImageProps>(
  (props, ref) => {
    const {
      fallbackSrc,
      fallback,
      src,
      srcSet,
      align,
      fit,
      loading,
      ignoreFallback,
      crossOrigin,
      ...rest
    } = props;

    /**
     * Defer to native `img` tag if `loading` prop is passed
     * @see https://github.com/chakra-ui/chakra-ui/issues/1027
     */
    const shouldIgnore =
      loading != null ||
      ignoreFallback ||
      (fallbackSrc === undefined && fallback === undefined); // if the user doesn't provide any kind of fallback we should ignore it

    const status = useImage({
      ...props,
      ignoreFallback: shouldIgnore,
    });

    const shared = {
      ref,
      objectFit: fit,
      objectPosition: align,
      ...(shouldIgnore ? rest : omit(rest, ["onError", "onLoad"])),
    };

    if (status !== "loaded") {
      /**
       * If user passed a custom fallback component,
       * let's render it here.
       */
      if (fallback) return fallback;

      // @ts-ignore
      return (
        <NativeImage
          className="chakra-image__placeholder"
          src={fallbackSrc}
          {...shared}
        />
      );
    }

    return (
      // @ts-ignore
      <NativeImage
        src={src}
        srcSet={srcSet}
        crossOrigin={crossOrigin}
        // loading={loading}
        className="chakra-image"
        {...shared}
      />
    );
  },
);
Image.displayName = "Image";

export interface ImgProps
  extends React.HTMLProps<HTMLImageElement>,
    NativeImageOptions {}

/**
 * Fallback component for most SSR users who want to use the native `img` with
 * support for chakra props
 */
export const Img = React.forwardRef<HTMLImageElement, ImgProps>(
  (props, ref) => (
    // @ts-ignore
    <NativeImage ref={ref} className="chakra-image" {...props} />
  ),
);

Img.displayName = "Img";
