import { Direction, Orientation, QAbstractSliderSignals, QIcon, QLabel } from "@nodegui/nodegui";
import { BoxView, GridColumn, GridRow, GridView, Slider, Text, useEventHandler } from "@nodegui/react-nodegui";
import React, { ReactElement, useContext, useEffect, useRef, useState } from "react";
import playerContext, { CurrentPlaylist } from "../context/playerContext";
import { shuffleArray } from "../helpers/shuffleArray";
import NodeMpv from "node-mpv";
import { getYoutubeTrack } from "../helpers/getYoutubeTrack";
import PlayerProgressBar from "./PlayerProgressBar";
import { random as shuffleIcon, play, pause, backward, forward, stop, heartRegular, heart } from "../icons";
import IconButton from "./shared/IconButton";
import showError from "../helpers/showError";
import useTrackReaction from "../hooks/useTrackReaction";

export const audioPlayer = new NodeMpv(
  {
    audio_only: true,
    auto_restart: true,
    time_update: 1,
    binary: process.env.MPV_EXECUTABLE ?? "/usr/bin/mpv",
    // debug: true,
    // verbose: true,
  },
  ["--ytdl-raw-options-set=format=140,http-chunk-size=300000"]
);

function Player(): ReactElement {
  const { currentTrack, currentPlaylist, setCurrentTrack, setCurrentPlaylist } = useContext(playerContext);
  const { reactToTrack, isFavorite } = useTrackReaction();
  const [isPaused, setIsPaused] = useState(true);
  const [volume, setVolume] = useState<number>(55);
  const [totalDuration, setTotalDuration] = useState(0);
  const [shuffle, setShuffle] = useState<boolean>(false);
  const [realPlaylist, setRealPlaylist] = useState<CurrentPlaylist["tracks"]>([]);
  const [isStopped, setIsStopped] = useState<boolean>(false);
  const playlistTracksIds = currentPlaylist?.tracks.map((t) => t.track.id);
  const volumeHandler = useEventHandler<QAbstractSliderSignals>(
    {
      sliderMoved: (value) => {
        setVolume(value);
      },
      sliderReleased: () => {
        localStorage.setItem("volume", volume.toString());
      },
    },
    []
  );
  const playerRunning = audioPlayer.isRunning();
  const titleRef = useRef<QLabel>();

  // initial Effect
  useEffect(() => {
    (async () => {
      try {
        if (!playerRunning) {
          await audioPlayer.start();
          await audioPlayer.volume(volume);
        }
      } catch (error) {
        showError(error, "[Failed starting audio player]: ");
      }
    })();

    return () => {
      if (playerRunning) {
        audioPlayer.quit().catch((e: any) => console.log(e));
      }
    };
  }, []);

  // track change effect
  useEffect(() => {
    (async () => {
      try {
        if (currentTrack && playerRunning) {
          const youtubeTrack = await getYoutubeTrack(currentTrack);
          await audioPlayer.load(youtubeTrack.youtube_uri, "replace");
          await audioPlayer.play();
          setIsPaused(false);
        }
        setIsStopped(false);
      } catch (error) {
        if (error.errcode !== 5) {
          setIsStopped(true);
          setIsPaused(true);
        }
        showError(error, "[Failure at track change]: ");
      }
    })();
  }, [currentTrack]);

  // changing shuffle to default
  useEffect(() => {
    setShuffle(false);
  }, [currentPlaylist])

  useEffect(() => {
    if (playerRunning) {
      audioPlayer.volume(volume);
    }
  }, [volume]);

  // for monitoring shuffle playlist
  useEffect(() => {
    if (currentPlaylist) {
      if (shuffle && realPlaylist.length === 0) {
        const shuffledTracks = shuffleArray(currentPlaylist.tracks);
        setRealPlaylist(currentPlaylist.tracks);
        setCurrentPlaylist({ ...currentPlaylist, tracks: shuffledTracks });
      } else if (!shuffle && realPlaylist.length > 0) {
        setCurrentPlaylist({ ...currentPlaylist, tracks: realPlaylist });
      }
    }
  }, [shuffle]);

  // live Effect
  useEffect(() => {
    if (playerRunning) {
      const statusListener = (status: { property: string; value: any }) => {
        if (status?.property === "duration") {
          setTotalDuration(status.value);
        }
      };
      const stopListener = () => {
        setIsStopped(true);
        setIsPaused(true);
        // go to next track
        if (currentTrack && playlistTracksIds && currentPlaylist?.tracks.length !== 0) {
          const index = playlistTracksIds?.indexOf(currentTrack.id) + 1;
          setCurrentTrack(currentPlaylist?.tracks[index > playlistTracksIds.length - 1 ? 0 : index].track);
        }
      };
      const pauseListener = () => {
        setIsPaused(true);
      };
      const resumeListener = () => {
        setIsPaused(false);
      };
      audioPlayer.on("status", statusListener);
      audioPlayer.on("stopped", stopListener);
      audioPlayer.on("paused", pauseListener);
      audioPlayer.on("resumed", resumeListener);
      return () => {
        audioPlayer.off("status", statusListener);
        audioPlayer.off("stopped", stopListener);
        audioPlayer.off("paused", pauseListener);
        audioPlayer.off("resumed", resumeListener);
      };
    }
  });

  const handlePlayPause = async () => {
    try {
      if ((await audioPlayer.isPaused()) && playerRunning) {
        await audioPlayer.play();
        setIsStopped(false);
        setIsPaused(false);
      } else {
        await audioPlayer.pause();
        setIsStopped(true);
        setIsPaused(true);
      }
    } catch (error) {
      showError(error, "[Track control failed]: ");
    }
  };

  const prevOrNext = (constant: number) => {
    if (currentTrack && playlistTracksIds && currentPlaylist) {
      const index = playlistTracksIds.indexOf(currentTrack.id) + constant;
      setCurrentTrack(currentPlaylist.tracks[index > playlistTracksIds?.length - 1 ? 0 : index < 0 ? playlistTracksIds.length - 1 : index].track);
    }
  };

  async function stopPlayback() {
    try {
      if (playerRunning) {
        setCurrentTrack(undefined);
        setCurrentPlaylist(undefined);
        await audioPlayer.stop();
      }
    } catch (error) {
      showError(error, "[Failed at audio-player stop]: ");
    }
  }

  const artistsNames = currentTrack?.artists?.map((x) => x.name);
  return (
    <GridView enabled={!!currentTrack} style="flex: 1; max-height: 120px;">
      <GridRow>
        <GridColumn width={2}>
          <Text ref={titleRef} wordWrap>
            {artistsNames && currentTrack
              ? `
            <p><b>${currentTrack.name}</b> - ${artistsNames[0]} ${artistsNames.length > 1 ? "feat. " + artistsNames.slice(1).join(", ") : ""}</p>
            `
              : `<b>Oh, dear don't waste time</b>`}
          </Text>
        </GridColumn>
        <GridColumn width={4}>
          <BoxView direction={Direction.TopToBottom} style={`max-width: 600px; min-width: 380px;`}>
            <PlayerProgressBar audioPlayer={audioPlayer} totalDuration={totalDuration} />

            <BoxView direction={Direction.LeftToRight}>
              <IconButton style={`background-color: ${shuffle ? "orange" : "rgba(255, 255, 255, 0.055)"}`} on={{ clicked: () => setShuffle(!shuffle) }} icon={new QIcon(shuffleIcon)} />
              <IconButton on={{ clicked: () => prevOrNext(-1) }} icon={new QIcon(backward)} />
              <IconButton on={{ clicked: handlePlayPause }} icon={new QIcon(isStopped || isPaused || !currentTrack ? play : pause)} />
              <IconButton on={{ clicked: () => prevOrNext(1) }} icon={new QIcon(forward)} />
              <IconButton icon={new QIcon(stop)} on={{ clicked: stopPlayback }} />
            </BoxView>
          </BoxView>
        </GridColumn>
        <GridColumn width={2}>
          <BoxView>
            <IconButton
              on={{
                clicked() {
                  if (currentTrack) {
                    reactToTrack({ added_at: Date.now().toString(), track: currentTrack });
                  }
                },
              }}
              icon={new QIcon(isFavorite(currentTrack?.id ?? "") ? heart : heartRegular)}
            />
            <Slider minSize={{ height: 20, width: 80 }} maxSize={{ height: 20, width: 100 }} hasTracking sliderPosition={volume} on={volumeHandler} orientation={Orientation.Horizontal} />
          </BoxView>
        </GridColumn>
      </GridRow>
    </GridView>
  );
}

export default Player;
