
// dependencies
import clsx from 'clsx';
import Link from 'next/link';
import { Blurhash } from 'react-blurhash';
import { useInViewport } from 'react-in-viewport';
import React, { useEffect, useMemo, useRef, useState } from 'react';

// cdn url
const imgBase = process.env.CDN_URL || 'https://cdn.craftiq.ai';

/**
 * component image
 *
 * @param props 
 */
const ComponentImage = (props = {}) => {
  // ref
  const measureRef = useRef(null);

  // in viewport
  const { inViewport } = useInViewport(measureRef);

  // width/height
  const [width, setWidth] = useState(props.width);
  const [error, setError] = useState(false);
  const [height, setHeight] = useState(props.height);
  const [imgLoaded, setImgLoaded] = useState(!!props.forceLoaded);
  const [mediaLoaded, setMediaLoaded] = useState(false);

  // memo
  const aspect = useMemo(() => {
    // check aspect
    if (props.aspect === true) {
      // automatically create aspect of image
      return props.image?.width && props.image?.height ? `${props.image.width}/${props.image.height}` : 1;
    }

    // if aspect is string or number
    return props.aspect;
  }, [props.aspect, props.url, props.image?.id]);
  const srcset = useMemo(() => {
    // check width
    if (!width || !height) return [];
    
    // url
    const baseUrl = props.url ? props.url : (props.image?.url ? `${imgBase}/${props.image.url}` : null);

    // check base url
    if (!baseUrl) return null;

    // new url
    const actualUrl = new URL(baseUrl);

    // add width/height
    actualUrl.searchParams.append('width', width);
    actualUrl.searchParams.append('height', height);
    actualUrl.searchParams.append('aspect_ratio', `${width}:${height}`);
    
    // check gravity
    let gravity;

    // set gravity
    if (props.gravity === 'top') gravity = 'north';

    // gravity
    if (gravity) actualUrl.searchParams.append('crop_gravity', gravity);

    // create srcset
    const srcset = [
      2,
      3,
      4,
    ].map((scale) => {
      // create url string
      const url = new URL(actualUrl.toString());

      // update width/height
      url.searchParams.set('width', parseInt(width * scale));
      url.searchParams.set('height', parseInt(height * scale));
      url.searchParams.set('aspect_ratio', `${parseInt(width * scale)}:${parseInt(height * scale)}`);

      // return url
      return {
        scale,
        url   : url.toString(),
        width : width * scale,
      };
    });

    // return full srcset
    return [
      {
        url : actualUrl.toString(),
        scale : 1,
        width,
      },
      ...srcset,
    ];
  }, [aspect, width, height, props.url, props.image?.id]);

  // use effect
  useEffect(() => {
    // if no onLoad
    if (!props.onLoad) return;

    // on load
    props.onLoad(imgLoaded || mediaLoaded);
  }, [imgLoaded, mediaLoaded]);
  useEffect(() => {
    // check height
    if (!measureRef?.current?.offsetHeight) return;

    // computed height
    const computedHeight = Math.floor(measureRef?.current?.offsetHeight);

    // if computed height
    if (isNaN(computedHeight)) return;
    
    // set height
    setHeight(computedHeight);
  }, [measureRef?.current?.offsetHeight]);
  useEffect(() => {
    // check width
    if (!measureRef?.current?.offsetWidth) return;

    // computed width
    const computedWidth = Math.floor(measureRef?.current?.offsetWidth);

    // if computed width
    if (isNaN(computedWidth)) return;
    
    // set width
    setWidth(computedWidth);
  }, [measureRef?.current?.offsetWidth]);

  // return jsx
  return (
    <Link
      ref={measureRef}
      href={`${imgBase}/${props.image?.url}`}
      target="_blank"
      className={clsx(
        props.className || '',
        'card flex relative overflow-hidden',
        props.modal
          ? 'cursor-pointer'
          : null,
        props.rounded
          ? '!rounded-full'
          : props.square
            ? '!rounded-none'
            : '!rounded-xl',
        props.grayscale
          ? 'grayscale'
          : null,
      )}
      style={{
        aspectRatio : aspect,
      }}
    >
      {/* BLURHASH */}
      {!!(props.image?.blurhash && !props.noBlurhash) && (
        <div
          className={clsx(
            'absolute w-full h-full -z-0 blurhash',
          )}
        >
          <Blurhash hash={props.image.blurhash} width="100%" height="100%" punch={1} />
        </div>
      )}
      {/* / BLURHASH */} 
      
      {/* IMAGE */}
      {!!(srcset?.length && (!inViewport || !mediaLoaded)) && (
        <img
          src={srcset?.[0]?.url}
          alt={props.alt || props.image?.alternativeText || props.image?.name || props.image?.prompt || props.url}
          srcSet={srcset?.slice(1).map((item) => `${item.url} ${item.scale}x`).join(', ')}
          className={clsx(
            'flex relative w-full h-full z-1',
            !props.noFade && 'transition-opacity duration-300 opacity-0',
            props.rounded
              ? '!rounded-full'
              : props.square
                ? '!rounded-none'
                : '!rounded-xl',
            !props.noFade && imgLoaded &&
              'opacity-100',
          )}
          onLoad={() => setImgLoaded(true)}
          onError={() => !imgLoaded && setError(true)}
        />
      )}
      {/* / IMAGE */}

      {/* WEBM */}
      {!!(props.image?.webm && inViewport && !props.noWebm) && (
        <video
          loop
          muted
          autoPlay
          playsInline
          src={`${imgBase}/${props.image.webm}`}
          className={clsx(
            'absolute top-0 left-0 w-full h-full z-1',
          )}
          onLoadedData={() => setMediaLoaded(true)}
        />  
      )} 
      {/* / WEBM */}
      
      {props.children}
    </Link>
  );
};

// export default
export default ComponentImage;