// @ts-nocheck

import Vue from "vue";
import Vuex from "vuex";

import { i18n } from "@/plugins/i18n";
import helpers from "../helpers";
import { axiosCacheInstance } from "@/plugins/axios";

// modules
import auth from "@/store/modules/auth";
import userModule from "@/store/modules/user";
import player from "@/store/modules/player";
import purchase from "@/store/modules/purchase";
import search from "@/store/modules/search";
import axios from "axios";

Vue.use(Vuex);

/**
 * Checks if a song is playable.
 * @param {*} song
 * @returns {Boolean}
 */

export default new Vuex.Store({
  modules: {
    auth,
    player,
    userModule,
    purchase,
    search,
  },
  state: {
    user: null,
    artist: null,
    messages: {},
    token: localStorage.getItem("token") || null,
    queue: [],
    playlists: null,
    dialog: null,
    screenWidth: window.innerWidth,
    settings: null,
    searchResultsPanel: false,
    notifications: null,
    plans: null,
    likedAlbums: null,
    currentlyPlayingType: null,
    currentPageId: 0,
    songMenu: null,
    // songContextMenu is a value to determine which song dialog should show
    songContextMenu: null,
    ads: null,
    followedPodcasts: null,
    ownSongs: null,
    followedArtists: null,
    followedPlaylists: null,
    openChatWith: null,
    friends: null,
    likedSongs: null,
    recentlyPlayed: null,
    registredPlayID: null,
    songs: null,
    fbLogoutFunction: null,
    addSongToPlaylist: null,
    sharableItem: null,
    showSharingDialog: false,
    currentLang: null,
    chooseLangDialog: false,
    availableLanguages: false,
    installPrompt: null,
    playerStatus: null,
    adPlayed: false,
    currentCampaign: null,
    theme: null,
  },
  mutations: {
    setCurrentCampaign(state, currentCampaign) {
      state.currentCampaign = currentCampaign;
    },
    setAdPlayed(state, adPlayed) {
      state.adPlayed = adPlayed;
    },
    shareItem(state, item) {
      state.sharableItem = item;
      state.showSharingDialog = true;
    },
    setRegistredPlayID(state, id) {
      state.registredPlayID = id;
    },
    hideSharingDialog(state) {
      state.showSharingDialog = false;
    },
    updateQueue(state, songs) {
      state.queue = songs;
    },
    setSongMenu(state, id) {
      state.songMenu == id ? (state.songMenu = null) : (state.songMenu = id);
    },
    /**
     * this setter makes sure that only one song dialog-box is oppened at a time
     * with the help of the song ID
     * @param {*} state
     * @param {*} id
     */
    setSongContextMenu(state, id) {
      state.songContextMenu == id
        ? // if the ID of the previous dialog is the same as the new one
          // the value of the variable will be null, hence the dialog will close
          (state.songContextMenu = null)
        : (state.songContextMenu = id);
    },
    pushIntoQueue(state, songs) {
      songs.map((song) => state.queue.push(song));
    },
    setToken(state, token) {
      state.token = token;
    },
    setMessages(state, messages) {
      state.messages = messages;
    },
    setSettings(state, settings) {
      state.settings = settings;
    },
    setTheme(state, theme) {
      state.theme = theme;
    },
    setFbLogoutFunction(state, fbLogoutFunction) {
      state.fbLogoutFunction = fbLogoutFunction;
    },
    setCurrentPageId(state) {
      state.currentPageId++;
    },
    setSearchResultsPanel(state, value) {
      state.searchResultsPanel = value;
    },
    setPlaylists(state, playlists) {
      state.playlists = playlists;
    },
    setUser(state, user) {
      state.user = user;
    },
    setArtist(state, artist) {
      state.artist = artist;
    },
    setCurrentLang(state, currentLang) {
      state.currentLang = currentLang;
    },
    setLikedSongs(state, songs) {
      state.likedSongs = songs.filter((song) => song);
    },
    setLikedAlbums(state, Albums) {
      state.likedAlbums = Albums;
    },
    setFollowedPodcasts(state, Podcasts) {
      state.followedPodcasts = Podcasts;
    },
    setRecentlyPlayed(state, Songs) {
      state.recentlyPlayed = Songs.filter((Song) => Song);
    },
    setOwnSongs(state, Songs) {
      state.ownSongs = Songs;
    },
    /**
     * Remove a song from the array of liked songs ( by the current logged user)
     * @param {*} state
     * @param {*} param1
     */
    spliceFromLikes(state, { id }) {
      const index = state.likedSongs.findIndex((x) => x.id == id);
      state.likedSongs.splice(index, 1);
    },
    pushIntoLikes(state, Song) {
      state.likedSongs.push(Song);
    },
    setDialog(state, value) {
      state.dialog = value;
    },
    setScreenWidth(state, value) {
      state.screenWidth = value;
    },
    setOpenChatWith(state, friend_id) {
      state.openChatWith = friend_id;
    },
    setAddSongToPlaylist(state, song) {
      state.addSongToPlaylist = song;
    },
    setUploadImage(state, img) {
      state.uploadImage = img;
    },
    setFollowedArtists(state, artists) {
      state.followedArtists = artists;
    },
    setFollowedPlaylists(state, playlists) {
      state.followedPlaylists = playlists;
    },
    setFriends(state, users) {
      state.friends = users;
    },
    setAds(state, ads) {
      state.ads = ads;
    },
    setNotifications(state, notifications) {
      state.notifications = notifications;
    },
    setPlans(state, plans) {
      state.plans = plans;
    },
    setChooseLangDialog(state, val) {
      state.chooseLangDialog = val;
    },
    setAvailableLanguages(state, val) {
      state.availableLanguages = val;
    },
    setInstallPrompt(state, e) {
      state.installPrompt = e;
    },
    setCurrentlyPlayingType(state, type) {
      state.currentlyPlayingType = type;
    },
    setCurrentlyPlayingTypeStatus(state, status) {
      state.currentlyPlayingType.status = status;
    },
    setPlayerStatus(state, status) {
      state.playerStatus = status;
    },
  },
  getters: {
    getCurrentCampaign(state) {
      return state.currentCampaign;
    },
    // getters for the state props.
    getQueue(state) {
      return state.queue;
    },
    getSharableItem(state) {
      return state.sharableItem;
    },
    getDialog(state) {
      return state.dialog;
    },
    getRegistredPlayID(state) {
      return state.registredPlayID;
    },
    getSettings(state) {
      return state.settings;
    },
    getTheme(state) {
      return state.theme;
    },
    getSongMenu(state) {
      return state.songMenu;
    },
    getSongContextMenu(state) {
      return state.songContextMenu;
    },
    getCurrentLang(state) {
      return state.currentLang;
    },
    getAds(state) {
      return state.ads;
    },
    getAdPlayed(state) {
      return state.adPlayed;
    },
    getSearchResultsPanel(state) {
      return state.searchResultsPanel;
    },
    getScreenWidth(state) {
      return state.screenWidth;
    },
    isLogged(state) {
      return state.token !== null;
    },
    getToken(state) {
      return state.token;
    },
    getMessages(state) {
      return state.messages;
    },
    getCurrentPageId(state) {
      return state.currentPageId;
    },
    getPlaylists(state) {
      return state.playlists;
    },
    getOwnSongs(state) {
      return state.ownSongs;
    },
    getLikedSongs(state) {
      return state.likedSongs;
    },
    getRecentlyPlayed(state) {
      return state.recentlyPlayed;
    },
    getUser(state) {
      return state.user;
    },
    getArtist(state) {
      return state.artist;
    },
    getNotifications(state) {
      return state.notifications;
    },
    getPlans(state) {
      return state.plans;
    },
    getLikedAlbums(state) {
      return state.likedAlbums;
    },
    getFollowedPodcasts(state) {
      return state.followedPodcasts;
    },
    getUploadImage(state) {
      return state.uploadImage;
    },
    getAddSongToPlaylist(state) {
      return state.addSongToPlaylist;
    },
    getOpenChatWith(state) {
      return state.openChatWith;
    },
    getFollowedArtists(state) {
      return state.followedArtists;
    },
    getFollowedPlaylists(state) {
      return state.followedPlaylists;
    },
    getFriends(state) {
      return state.friends;
    },
    /**
     * Checks if a user has a certain role.
     * @param {*} state
     * @return {Boolean}
     */
    isUser(state) {
      return function (role) {
        if (role === "admin" && state.user.is_admin) {
          return true;
        }
        return state.user.roles.some((r) => r.name === role);
      };
    },
    /**
     * Checks if a song is already liked by the user.
     * @param {*} state
     * @return {Boolean}
     */
    isLiked(state: any): boolean {
      return function ({ id, type }) {
        const getter = "liked" + type[0].toUpperCase() + type.slice(1) + "s"; // Build the getter name dynamically
        return (state[getter] || []).some((x) => (x ? x.id == id : false));
      };
    },
    /**
     * Checks if a song is already liked by the user.
     * @param {*} state
     * @return {Boolean}
     */
    isFollowed(state: any): boolean {
      return function ({ id, type }) {
        const getter = "followed" + type[0].toUpperCase() + type.slice(1) + "s";
        return (state[getter] || []).some((x) => (x ? x.id == id : false));
      };
    },
    getChooseLangDialog(state) {
      return state.chooseLangDialog;
    },
    getAvailableLanguages(state) {
      return state.availableLanguages;
    },
    getInstallPrompt(state) {
      return state.installPrompt;
    },
    getCurrentlyPlayingType(state) {
      return state.currentlyPlayingType;
    },
    getPlayerStatus(state) {
      return state.playerStatus;
    },
  },
  actions: {
    /**
     * Register a play on the database for stats.
     * @param {*} context
     * @param {*} id
     */
    fetchMessages(context, locale) {
      return new Promise((resolve, reject) => {
        axiosCacheInstance
          .get("/api/messages/" + locale)
          .then((msgs) => {
            const messages = {};
            messages.en = msgs.data.en;
            i18n.setLocaleMessage("en", messages.en);
            if (locale !== "en") {
              messages[locale] = msgs.data[locale];
              i18n.setLocaleMessage(locale, messages[locale]);
            }
            i18n.locale = locale;
            if (
              context.getters.getAvailableLanguages &&
              context.getters.getAvailableLanguages.length
            ) {
              context.commit(
                "setCurrentLang",
                context.getters.getAvailableLanguages.find(
                  (lang) => lang.locale == locale
                )
              );
            }
            context.commit("setMessages", messages);
          })
          .then((res) => {
            resolve(res);
          })
          .catch((e) => {
            reject(e);
          });
      });
    },
    /**
     * Get the user notifications.
     * @param {*} context
     */
    fetchNotifications(context) {
      return new Promise((resolve, reject) => {
        axios
          .get("/api/user/notifications")
          .then((res) => {
            context.commit("setNotifications", res.data);
          })
          .catch((e) => {
            reject(e);
          });
      });
    },
    /**
     * Get the user notifications.
     * @param {*} context
     */
    fetchPlans(context) {
      return new Promise((resolve, reject) => {
        axios
          .get("/api/plans")
          .then((res) => {
            context.commit("setPlans", res.data);
            resolve();
          })
          .catch((e) => {
            reject(e);
          });
      });
    },

    /**
     * Get the current logged user data.
     * @param {*} context
     */
    fetchUser(context) {
      return new Promise((res, rej) => {
        axiosCacheInstance
          .get("/api/user")
          .then((result) => {
            context.commit("setUser", result.data);
            context.dispatch("likes", "song");
            context.dispatch("likes", "album");
            context.dispatch("follows", "artist");
            if (context.getters.getSettings.enablePodcasts) {
              context.dispatch("follows", "podcast");
            }
            context.dispatch("fetchPlaylists");
            context.dispatch("fetchOwnSongs");
            context.dispatch("fetchNotifications");
            context.dispatch("fetchPurchases");
            //
            context.dispatch("fetchFriends");
            context.commit("purchase/setCart", result.data.cart);

            res(result.data);
          })
          .catch(() => {
            context.commit("setUser", null);
            rej();
          });
      });
    },
    /**
     * Get the current logged artist data.
     * @param {*} context
     */
    async fetchArtist(context) {
      try {
        let res = await axios.get("/api/artist");
        context.commit("setArtist", res.data);
      } catch (e) {
        context.commit("setArtist", {});
      }
    },

    registerPlay(
      context,
      { id, type, label, duration, origin, artist_id = "" }
    ) {
      if (
        context.getters.getSettings.ga4 &&
        context.getters.getSettings.analytics_play_event
      ) {
        emitAnalyticsEvent({
          action: "play",
          category: type,
          label: label,
        });
      }
      return new Promise((resolve, reject) => {
        axios
          .post(
            "/api" +
              (context.getters.getUser ? "/user" : "") +
              "/register-play",
            {
              id,
              type,
              artist_id,
              duration,
              origin,
            }
          )
          .then((res) => {
            context.commit("setRegistredPlayID", res.data);
            resolve(res);
          })
          .catch((e) => {
            reject(e);
          });
      });
    },
    /**
     * Play an album.
     * @param {*} context
     * @param {*} songs
     */
    async playAlbum(context, { item }) {
      context.dispatch("registerPlay", { ...item, label: item.title });
      var songs;
      if (!item.songs) {
        await axios.get("/api/album/" + item.id + "/songs").then((res) => {
          songs = res.data;
        });
      } else {
        songs = item.songs;
      }

      context.dispatch("updateQueue", {
        content: songs,
        reset: true,
      });
    },

    async playArtist(context, { item }) {
      var songs;
      if (!item.songs) {
        await axios.get("/api/artist/" + item.id + "/songs").then((res) => {
          songs = res.data;
        });
      } else {
        songs = item.songs;
      }
      if (songs.length) {
        context.dispatch("registerPlay", { ...item, label: item.title });
        context.dispatch("updateQueue", {
          content: songs,
          reset: true,
        });
      } else {
        // TODO: notify artist has no songs
      }
    },
    /**
     * Play a podcast.
     * @param {*} context
     * @param {*} songs
     */
    async playPodcast(context, { item }) {
      context.dispatch("registerPlay", {
        ...item,
        label: item.title,
      });
      // fetch item episodes if they are not fetched
      if (!item.episodes) {
        var episodes = [];
        await axios.get("/api/podcasts/" + item.id).then((res) => {
          episodes = res.data.episodes;
        });
      } else {
        var episodes = item.episodes;
      }
      context.dispatch("updateQueue", {
        content: episodes,
        reset: true,
      });
    },
    /**
     * Play a playlist.
     * @param {*} context
     * @param {*} songs
     */
    async playPlaylist(context, { item }) {
      context.dispatch("registerPlay", {
        ...item,
        label: item.title,
      });

      if (!item.songs) {
        var songs = [];
        await axios.get("/api/playlists/" + item.id).then((res) => {
          songs = res.data.songs;
        });
      } else {
        var songs = item.songs;
      }

      context.dispatch("updateQueue", {
        content: songs,
        reset: true,
      });
    },
    /**
     * Play a radio station.
     * @param {*} context
     * @param {*} stream
     */
    playRadioStation(context, { item }) {
      context.dispatch("updateQueue", {
        content: [item],
        reset: true,
      });
    },

    /**
     * Play a radio station.
     * @param {*} context
     * @param {*} stream
     */
    playEpisode(context, { item, reset }) {
      // registering the plays will take place on the player
      context.dispatch("updateQueue", { content: [item], reset });
    },
    playSong(context, { item, reset }) {
      // registering the plays will take place on the player
      context.dispatch("updateQueue", { content: [item], reset });
    },
    /**
     * Reset the user status when he compeleted listening to an audio.
     */
    endPlay(context) {
      return new Promise((resolve, reject) => {
        axios
          .post("/api/user/end-play/" + context.getters.getRegistredPlayID)
          .then((res) => {
            resolve(res);
          })
          .catch((e) => {
            reject(e);
          });
      });
    },
    /**
     * Download a song.
     * @param {*} context
     * @param {*} param1
     */
    downloadAudio(context, { id, file_name, type }) {
      return new Promise(async (res) => {
        if (!context.getters.isLogged) {
          // if the user is not logged
          await helpers.loginOrCancel();
        }
        axios
          .get(
            "/api/download-" +
              (type === "episode" ? "episode" : "song") +
              "/" +
              id,
            {
              responseType: "arraybuffer",
            }
          )
          .then(async (response) => {
            const url = window.URL.createObjectURL(new Blob([response.data]));
            // creating an anchor to trigger the URL
            const link = document.createElement("a");
            link.href = url;
            link.setAttribute("download", file_name);
            document.body.appendChild(link);
            // Execute the link
            link.click();
          })
          .finally(() => res());
      });
    },
    updateLang(context, lang) {
      context.dispatch("fetchMessages", lang.locale).then(() => {
        // context.commit("setCurrentLang", lang);
        localStorage.setItem("pref-locale", lang.locale);
        context.commit("setChooseLangDialog", false);
      });
    },
  },
});
