import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';

import { ASSET_FIELD_PROP_TYPE, TRANSFORM_PROP_TYPES, getImageFromField } from '../utils';

const TRANSPARENT_INLINE_IMAGE =
  'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';

const getRetinaTransformOptions = (options) => ({
  ...options,
  ...(options.w ? { w: options.w * 2 } : {}),
  ...(options.h ? { h: options.h * 2 } : {})
});

const getSrcSet = (getImageUrl, url, options, retina = false) => {
  const defaultUrl = getImageUrl(url, options);

  if (!retina) {
    return defaultUrl;
  }

  const retinaUrl = getImageUrl(url, getRetinaTransformOptions(options));

  return `${defaultUrl} 1x, ${retinaUrl} 2x`;
};

const addWebpSources = (sources, url, transform) => [
  // add webp version for each source
  ...sources.reduce(
    (agg, source) => [
      ...agg,
      {
        ...source,
        type: 'image/webp',
        transform: {
          ...source.transform,
          fm: 'webp'
        }
      },
      source
    ],
    []
  ),

  // webp version for the base image
  {
    source: url,
    type: 'image/webp',
    transform: {
      ...transform,
      fm: 'webp'
    }
  }
];

const defaultRenderImg = ({ imgRef, srcset, src, sizes, loading, style, ...props }) => (
  <img
    alt=""
    srcSet={srcset}
    src={src}
    sizes={sizes}
    loading={loading}
    style={style}
    {...(imgRef ? { ref: imgRef } : {})}
    {...props}
  />
);

export const Picture = (props) => {
  const {
    image,
    alt,
    transform,
    sources,
    retina,
    webp,
    render,
    imgRef,
    preload,
    getImageUrl,
    ...restProps
  } = props;

  const url = getImageFromField(image);

  if (!url) {
    return null;
  }

  const src = getImageUrl(url, transform);
  const processedSources = webp ? addWebpSources(sources, url, transform) : sources;

  // Generate src set for retina
  const retinaProps = retina
    ? {
        src: TRANSPARENT_INLINE_IMAGE,
        srcSet: getSrcSet(getImageUrl, url, transform, retina)
      }
    : { src };

  // Set src to transparent png to avoid double downloads on not supported browsers
  const sourcesProps =
    processedSources?.length > 0
      ? { src: TRANSPARENT_INLINE_IMAGE, srcSet: retinaProps.srcSet || src }
      : { src };

  // set width if transform is present
  const width = transform?.w;

  return (
    <picture>
      {processedSources?.map(({ media, sizes, srcSet, transform: sourceTransform, type }) => {
        const sourceSrcSet = srcSet || getSrcSet(getImageUrl, url, sourceTransform, retina);
        return (
          <React.Fragment key={`${media || 'default'}-${type || 'image'}`}>
            {preload && (
              <Helmet>
                <link
                  rel="preload"
                  as="image"
                  {...(media && { media })}
                  href={src}
                  imageSrcSet={sourceSrcSet}
                  imageSizes={sizes}
                />
              </Helmet>
            )}
            <source media={media} srcSet={sourceSrcSet} sizes={sizes} type={type} />
          </React.Fragment>
        );
      })}
      {/** Preload default image only if there are no sources to prevent multiple downloads */}
      {preload && processedSources.length === 0 && (
        <Helmet>
          <link
            rel="preload"
            as="image"
            href={retinaProps.src || sourcesProps.src}
            imageSrcSet={retinaProps.srcSet || sourcesProps.srcSet}
          />
        </Helmet>
      )}
      {render({
        alt,
        imgRef,
        width,
        ...retinaProps,
        ...sourcesProps,
        ...restProps
      })}
    </picture>
  );
};

Picture.defaultProps = {
  image: null,
  alt: '',
  transform: {},
  sources: [],
  render: defaultRenderImg,
  retina: false,
  webp: false,
  imgRef: null,
  preload: false
};

Picture.propTypes = {
  /** Image field */
  image: ASSET_FIELD_PROP_TYPE,

  /** Image alt text */
  alt: PropTypes.string,

  /** Contentful Image API transform */
  transform: TRANSFORM_PROP_TYPES,

  /** Picture sources */
  sources: PropTypes.arrayOf(
    PropTypes.shape({
      media: PropTypes.string,
      transform: TRANSFORM_PROP_TYPES
    })
  ),

  /** Render prop */
  render: PropTypes.func,

  /** Retina flag */
  retina: PropTypes.bool,

  /** Webp flag */
  webp: PropTypes.bool,

  /** Image ref */
  imgRef: PropTypes.shape({
    current: PropTypes.object // eslint-disable-line react/forbid-prop-types
  }),

  /** Preload image */
  preload: PropTypes.bool,

  getImageUrl: PropTypes.func.isRequired
};
