import * as React from 'react';
import { FC, useRef, useEffect, useState, useContext, MouseEvent } from 'react';
import { style } from 'typestyle';
import { IMedia } from '../../../../../../types';
import Play from '../../../../../Icons/PlayPause/Play';
import VideoDurationBar from '../../../../components/VideoDurationBar';
import { StoreContext } from '../../../../../../services/store';
import Spinner from '../../../../../Icons/Spinner';

interface VideoPlayerProps {
  post: IMedia;
  /** Hint the browser to buffer this video ahead of time (e.g. for neighbour slides). */
  preload?: boolean;
  /** Disable video src to prevent idle neighbours from loading. */
  srcEnabled?: boolean;
  /** Called when the current (non-preload) video is ready to render. */
  onVideoReady?: () => void;
  isMuted: boolean;
  isPlaying: boolean;
  togglePlay: () => void;
  objectFit?: 'contain' | 'cover';
  /** When true, always shows the duration bar.
   * When undefined, falls back to showing it only when mobile_player_product_cta is 'none'. */
  showDurationBar?: boolean;
  /** When true, clicking the video element will not toggle play/pause.
   * Use on desktop where tap navigation handles clicks. */
  disableClickToPlay?: boolean;
  /** When true, the duration bar is anchored to the top edge instead of the bottom. */
  durationBarAtTop?: boolean;
  /** Called when the video reaches its end (only fires when provided; disables loop). */
  onVideoEnded?: () => void;
}

const VideoPlayer: FC<VideoPlayerProps> = ({
  post,
  preload = false,
  srcEnabled = true,
  onVideoReady,
  isMuted,
  isPlaying,
  togglePlay,
  objectFit = 'contain',
  showDurationBar,
  disableClickToPlay = false,
  durationBarAtTop = false,
  onVideoEnded,
}) => {
  const { data } = useContext(StoreContext);
  const productCta = data?.settings?.mobile_player_product_cta ?? 'packshot';

  const videoRef = useRef<HTMLVideoElement>(null);
  const [autoplayPrevented, setAutoplayPrevented] = useState(false);
  const [isBuffering, setIsBuffering] = useState(true);
  const [currentTime, setCurrentTime] = useState(0);
  const [duration, setDuration] = useState(0);

  const videoSrc = srcEnabled ? post.video : undefined;

  const handleTimeUpdate = () => {
    const video = videoRef.current;
    if (!video || !video.duration) return;
    setCurrentTime(video.currentTime);
    setDuration(video.duration);
  };

  const handleSeek = (time: number) => {
    const video = videoRef.current;
    if (!video) return;
    video.currentTime = time;
    setCurrentTime(time);
  };

  const handleCanPlay = () => {
    setIsBuffering(false);
    // Notify MediaContainer so it can unlock neighbour src fetching.
    if (!preload) onVideoReady?.();
  };

  // Manage src gating and playback together.
  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    if (!srcEnabled) {
      video.pause();
      video.removeAttribute('src');
      video.load();
      setIsBuffering(true);
      return;
    }

    let cancelled = false;

    if (isPlaying) {
      // Force load if the video has no buffered data yet.
      if (video.readyState === 0) video.load();

      video
        .play()
        .then(() => { if (!cancelled) setAutoplayPrevented(false); })
        .catch((err) => {
          if (cancelled) return;
          // Ignore harmless pause/play races;
          // Only NotAllowedError is a genuine browser autoplay block.
          if (err?.name === 'NotAllowedError') {
            setAutoplayPrevented(true);
            togglePlay();
          }
        });
    } else {
      video.pause();
    }

    // eslint-disable-next-line consistent-return
    return () => { cancelled = true; };
  }, [srcEnabled, isPlaying]);

  useEffect(() => {
    if (videoRef.current) videoRef.current.muted = isMuted;
  }, [isMuted]);

  const handlePlayButtonClick = (e: MouseEvent) => {
    e.stopPropagation();
    e.preventDefault();
    togglePlay();
  };

  return (
    <div className={styles.container}>
      <video
        ref={videoRef}
        src={videoSrc}
        muted={isMuted}
        playsInline
        loop={!onVideoEnded}
        preload={videoSrc ? 'auto' : 'none'}
        className={styles.media}
        style={{ objectFit }}
        onClick={disableClickToPlay ? undefined : togglePlay}
        onCanPlay={handleCanPlay}
        onWaiting={() => setIsBuffering(true)}
        onPlaying={() => setIsBuffering(false)}
        onTimeUpdate={handleTimeUpdate}
        onEnded={onVideoEnded}
      />

      {!preload && (showDurationBar ?? productCta === 'none') ? (
        <VideoDurationBar
          currentTime={currentTime}
          duration={duration}
          onSeek={handleSeek}
          positionTop={durationBarAtTop}
        />
      ) : null}

      {(autoplayPrevented || !isPlaying) && !preload ? (
        <div className={styles.playButtonContainer}>
          <div className={styles.playButtonWrapper}>
            <div
              className={`${styles.playButton} playButton`}
              onClick={handlePlayButtonClick}
            >
              <Play size={120} color={'rgba(255, 255, 255, 0.7)'} />
            </div>
          </div>
        </div>
      ) : null}

      {isPlaying && isBuffering && !preload ? (
        <div className={styles.playButtonContainer}>
          <Spinner size={48} borderWidth={4} />
        </div>
      ) : null}
    </div>
  );
};

export default VideoPlayer;

const styles = {
  container: style({
    position: 'relative',
    width: '100%',
    height: '100%',
  }),
  media: style({
    width: '100%',
    height: '100%',
  }),
  playButtonContainer: style({
    position: 'absolute',
    top: '0',
    left: '0',
    width: '100%',
    height: '100%',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    pointerEvents: 'none',
  }),
  playButtonWrapper: style({
    display: 'inline-block',
    lineHeight: 0,
    fontSize: 0,
    pointerEvents: 'auto',
  }),
  playButton: style({
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    cursor: 'pointer',
  }),
};
