'use client';

import React, {
  useRef,
  useEffect,
  forwardRef,
  ComponentProps,
} from 'react';
import { MuxBackgroundVideo as MuxBackgroundVideoCore } from './mux-background-video.js';

export type MuxBackgroundVideoProps = ComponentProps<'video'> &
  MuxBackgroundVideoCore['config'];

export type MuxBackgroundVideoRef = Partial<MuxBackgroundVideoCore['display']>;

export const MuxBackgroundVideo = forwardRef<
  MuxBackgroundVideoRef,
  MuxBackgroundVideoProps
>(
  (
    {
      className,
      style,
      children,
      src,
      audio,
      debug,
      maxResolution,
      preload,
      ...props
    },
    ref
  ) => {
    const videoRef = useRef<HTMLVideoElement>(null);
    const muxRef = useRef<MuxBackgroundVideoCore | null>(null);

    useEffect(() => {
      if (!videoRef.current) return;

      muxRef.current ??= new MuxBackgroundVideoCore();
      muxRef.current.display = videoRef.current;
      muxRef.current.config = {
        audio,
        debug,
        maxResolution,
        preload,
      };
      muxRef.current.src = src ?? '';

      return () => {
        if (muxRef.current) {
          muxRef.current = null;
        }
      };
    }, [src, audio, debug, maxResolution, preload]);

    return (
      <div
        className={`${className ? `${className} ` : ''}mux-background-video`}
        style={style}
      >
        <style>{
          /*css*/ `
          .mux-background-video {
            position: relative;
            width: 100%;
            height: 100%;
            object-fit: cover;
          }

          .mux-background-video > video {
            position: absolute;
            inset: 0;
            width: 100%;
            height: 100%;
            object-fit: inherit;
          }

          .mux-background-video > :where(img, picture) {
            width: 100%;
            height: 100%;
            object-fit: inherit;
          }
        `
        }</style>
        {children}
        <video
          ref={useComposedRefs(videoRef, ref)}
          muted
          autoPlay
          loop
          playsInline
          disableRemotePlayback
          disablePictureInPicture
          {...props}
        />
      </div>
    );
  }
);

MuxBackgroundVideo.displayName = 'MuxBackgroundVideo';

// useComposedRefs.ts
type PossibleRef<T> = React.Ref<T> | undefined;

/**
 * Set a given ref to a given value
 * This utility takes care of different types of refs: callback refs and RefObject(s)
 */
function setRef<T>(ref: PossibleRef<T>, value: T): (() => void) | void | undefined {
  if (typeof ref === 'function') {
    return ref(value);
  } else if (ref !== null && ref !== undefined) {
    (ref as React.MutableRefObject<T>).current = value;
  }
}

/**
 * A utility to compose multiple refs together
 * Accepts callback refs and RefObject(s)
 */
function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
  return (node) => {
    let hasCleanup = false;
    const cleanups = refs.map((ref) => {
      const cleanup = setRef(ref, node);
      if (!hasCleanup && typeof cleanup == 'function') {
        hasCleanup = true;
      }
      return cleanup;
    });

    // React <19 will log an error to the console if a callback ref returns a
    // value. We don't use ref cleanups internally so this will only happen if a
    // user's ref callback returns a value, which we only expect if they are
    // using the cleanup functionality added in React 19.
    if (hasCleanup) {
      return () => {
        for (let i = 0; i < cleanups.length; i++) {
          const cleanup = cleanups[i];
          if (typeof cleanup == 'function') {
            cleanup();
          } else {
            setRef(refs[i], null);
          }
        }
      };
    }
  };
}

/**
 * A custom hook that composes multiple refs
 * Accepts callback refs and RefObject(s)
 */
function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
  // eslint-disable-next-line react-hooks/exhaustive-deps
  return React.useCallback(composeRefs(...refs), refs);
}
