/* eslint-disable @typescript-eslint/ban-ts-comment */
import Hls from "hls.js";
import { useEffect, useMemo, useRef, useState } from "react";
import { FormattedMessage } from "react-intl";
import { StreamState } from "..";
import { Icon } from "./icon";
import { ProgressBar } from "./progress-bar";
import { Menu, MenuItem } from "@szhsin/react-menu";

export enum VideoStatus {
  Online = "online",
  Offline = "offline",
}

export interface VideoPlayerProps {
  stream?: string;
  status?: string;
  poster?: string;
}

export default function LiveVideoPlayer(props: VideoPlayerProps) {
  const video = useRef<HTMLVideoElement>(null);
  const hlsObj = useRef<Hls>(null);
  const streamCached = useMemo(() => props.stream, [props.stream]);
  const [status, setStatus] = useState<VideoStatus>();
  const [src, setSrc] = useState<string>();
  const [levels, setLevels] = useState<Array<{ level: number; height: number }>>();
  const [level, setLevel] = useState<number>(-1);
  const [playState, setPlayState] = useState<"loading" | "playing" | "paused">("loading");
  const [volume, setVolume] = useState(1);
  const [position, setPosition] = useState<number>();
  const [maxPosition, setMaxPosition] = useState<number>();

  useEffect(() => {
    if (streamCached && video.current) {
      if (Hls.isSupported()) {
        try {
          const hls = new Hls({
            enableWorker: true,
            lowLatencyMode: true,
            backBufferLength: 90,
          });
          hls.loadSource(streamCached);
          hls.attachMedia(video.current);
          hls.on(Hls.Events.ERROR, (event, data) => {
            console.debug(event, data);
            const errorType = data.type;
            if (errorType === Hls.ErrorTypes.NETWORK_ERROR && data.fatal) {
              hls.stopLoad();
              hls.detachMedia();
              setStatus(VideoStatus.Offline);
            }
          });
          hls.on(Hls.Events.MANIFEST_PARSED, () => {
            setStatus(VideoStatus.Online);
            setLevels([
              {
                level: -1,
                height: 0,
              },
              ...hls.levels.map((a, i) => ({
                level: i,
                height: a.height,
              })),
            ]);
          });
          hls.on(Hls.Events.LEVEL_SWITCHING, (_, l) => {
            console.debug("HLS Level Switch", l);
            setMaxPosition(l.details?.totalduration);
          });
          // @ts-ignore Can write anyway
          hlsObj.current = hls;
          return () => {
            // @ts-ignore Can write anyway
            hlsObj.current = null;
            hls.destroy();
          };
        } catch (e) {
          console.error(e);
          setStatus(VideoStatus.Offline);
        }
      } else {
        setSrc(streamCached);
        setStatus(VideoStatus.Online);
        video.current.muted = true;
        video.current.load();
      }
    }
  }, [video, streamCached, props.status]);

  useEffect(() => {
    if (hlsObj.current) {
      hlsObj.current.nextLevel = level;
    }
  }, [hlsObj, level]);

  useEffect(() => {
    if (video.current) {
      video.current.onplaying = () => setPlayState("playing");
      video.current.onpause = () => setPlayState("paused");
      video.current.onseeking = () => setPlayState("loading");
      video.current.onplay = () => setPlayState("loading");
      video.current.onvolumechange = () => setVolume(video.current?.volume ?? 1);
      video.current.ontimeupdate = () => setPosition(video.current?.currentTime);
    }
  }, [video]);

  useEffect(() => {
    if (video.current) {
      video.current.volume = volume;
    }
  }, [video, volume]);

  function playStateToIcon() {
    switch (playState) {
      case "playing":
        return "pause";
      case "paused":
        return "play";
      case "loading":
        return "loading";
    }
  }

  function togglePlay() {
    if (video.current) {
      if (playState === "playing") {
        video.current.pause();
      } else if (playState === "paused") {
        video.current.play();
      }
    }
  }

  function levelName(l: number) {
    if (l === -1) {
      return <FormattedMessage defaultMessage="AUTO" id="o8pHw3" />;
    } else {
      const h = levels?.find(a => a.level === l)?.height;
      return <FormattedMessage defaultMessage="{n}p" id="YagVIe" values={{ n: h }} />;
    }
  }

  return (
    <div className="relative">
      {status === VideoStatus.Online && (
        <div
          className="absolute opacity-0 hover:opacity-90 transition-opacity w-full h-full z-20 bg-[#00000055]"
          onClick={() => togglePlay()}>
          <div className="absolute w-full h-full flex items-center justify-center pointer">
            <Icon name={playStateToIcon()} size={80} className={playState === "loading" ? "animate-spin" : ""} />
          </div>
          <div
            className="absolute flex items-center gap-1 bottom-0 w-full bg-primary h-[40px]"
            onClick={e => e.stopPropagation()}>
            <div className="flex grow gap-1 items-center">
              <div className="px-5 py-2 pointer" onClick={() => togglePlay()}>
                <Icon name={playStateToIcon()} className={playState === "loading" ? "animate-spin" : ""} />
              </div>
              <div className="px-3 py-2 uppercase font-bold tracking-wide hover:bg-primary-hover">{props.status}</div>
              {props.status === StreamState.Ended && maxPosition !== undefined && position !== undefined && (
                <ProgressBar
                  value={position / maxPosition}
                  setValue={v => {
                    const ct = maxPosition * v;
                    if (video.current) {
                      video.current.currentTime = ct;
                    }
                    setPosition(ct);
                  }}
                  marker={<div className="w-[16px] h-[16px] mt-[-8px] rounded-full bg-white"></div>}
                  style={{ width: "100%", height: "4px" }}
                />
              )}
            </div>
            <div className="flex gap-1 items-center h-full py-2">
              <Icon name="volume" />
              <ProgressBar value={volume} setValue={v => setVolume(v)} style={{ width: "100px", height: "100%" }} />
            </div>
            <div>
              <Menu
                direction="top"
                align="center"
                menuButton={<div className="px-3 py-2 tracking-wide pointer">{levelName(level)}</div>}
                menuClassName="bg-primary w-fit">
                {levels?.map(v => (
                  <MenuItem
                    value={v.level}
                    key={v.level}
                    onClick={() => setLevel(v.level)}
                    className="bg-primary px-3 py-2 text-white">
                    {levelName(v.level)}
                  </MenuItem>
                ))}
              </Menu>
            </div>
            <div
              className="px-3 py-2 pointer"
              onClick={() => {
                if (video.current) {
                  video.current.requestFullscreen();
                }
              }}>
              <Icon name="fullscreen" size={24} />
            </div>
          </div>
        </div>
      )}
      {status === VideoStatus.Offline && (
        <div className="absolute w-full h-full z-20 bg-[#000000aa] flex items-center justify-center text-3xl font-bold uppercase">
          <FormattedMessage defaultMessage="Offline" id="7UOvbT" />
        </div>
      )}
      <video className="z-10" ref={video} autoPlay={true} poster={props.poster} src={src} playsInline={true} />
    </div>
  );
}
