<template>
  <!-- Затемнение общее на весь флоу: у диалогов бэкдроп прозрачный, иначе при смене
       попапа их подложки складываются и фон моргает. -->
  <div
    v-if="isPlaylistAdd || isPlaylistEditOpen"
    class="playlist-main__decorate"
  />

  <PlaylistAdd
    :loading-post-playlist="loadingPostPlaylist"
    :loading-user-playlists="loadingUserPlaylists"
    :loading-post-video="loadingPostVideo"
    :user-playlists="userPlaylists"
    @execute-user-playlists="getUserPlaylists"
    @save-video-to-playlist="saveVideoToPlaylist"
  />

  <PlaylistEdit
    :loading-playlist-delete="loadingPlaylistDelete"
    :loading-playlists="loadingUserPlaylists"
    :loading-refresh="loadingRefresh"
    @delete-playlist="deletePlaylist"
    @save-edit="saveEditPlaylist"
  />

  <PlaylistDeleteVideo
    :loading-delete-video="loadingDeleteVideo"
    :loading-playlist-videos="loadingPlaylistsVideos"
    @delete-video="deleteVideo"
  />
</template>

<script setup lang="ts">
import { PlaylistsApiService } from '../../services/api/playlists.service';
import type { Language, PlaylistType } from '../../runtime';
import { AsyncData } from '../../runtime';
import { useI18n } from 'vue-i18n';
import type { IPlaylistCard, IPlaylistVideoFormType, IPlaylistShort, PaginatedResponse } from '../../types';

const { locale } = useI18n();

const { isPlaylistAdd } = usePlaylistAdd();
const { isPlaylistEditOpen } = usePlaylistEdit();
const { show } = useSnackbar();
const { isPendingDelete, markPendingDelete, unmarkPendingDelete } = usePlaylistDelete();

const route = useRoute();

const {
  data,
  execute: executeUserPlaylists,
  loading: loadingUserPlaylists
} = useApiAction(PlaylistsApiService.getUserPlaylists, true);
const {
  execute: executePostPlaylist,
  loading: loadingPostPlaylist,
} = useApiAction(PlaylistsApiService.postPlaylist);
const {
  execute: executePostPlaylistVideo,
  loading: loadingPostVideo
} = useApiAction(PlaylistsApiService.postPlaylistVideo);
const {
  execute: executeVideos,
  loading: loadingPlaylistsVideos
} = useApiAction(PlaylistsApiService.getVideosByPlaylist, true);
const { execute: executeDeleteVideo, loading: loadingDeleteVideo } = useApiAction(PlaylistsApiService.deleteVideo);
const {
  execute: executeDeletePlaylist,
  loading: loadingPlaylistDelete
} = useApiAction(PlaylistsApiService.deletePlaylist);
const { execute: executeRefresh, loading: loadingRefresh } = useApiAction(PlaylistsApiService.refreshPlaylist);

/** Плейлисты с ещё не отправленным удалением в попапе добавления не показываем. */
const userPlaylists = computed<PaginatedResponse<IPlaylistCard> | null>(() => data.value
  ? { ...data.value, items: data.value.items.filter(item => !isPendingDelete(item.id)) }
  : null);

async function getUserPlaylists() {
  await executeUserPlaylists([locale.value as Language], AsyncData.GetPlaylists);
}

async function saveVideoToPlaylist(event: {
  form: IPlaylistVideoFormType,
  id: string,
  newPlaylist?: IPlaylistShort
}) {
  const playlistIds = event.form.playlistId ? [...event.form.playlistId] : [];

  if (event.newPlaylist) {
    await executePostPlaylist([event.newPlaylist.name, event.newPlaylist.type]);
    await executeUserPlaylists([locale.value as Language], AsyncData.GetPlaylists);
    const newId = data.value?.items?.find(item => item.name === event.newPlaylist!.name)?.id;
    if (newId) playlistIds.push(newId);
  }

  const form = { ...event.form, playlistId: playlistIds };
  await executePostPlaylistVideo([form]);

  const currentPlaylistId = String(route.params['playlistId'] ?? '');
  if (playlistIds.includes(currentPlaylistId)) {
    await executeVideos([currentPlaylistId, {
      page: 1,
    }, locale.value as Language], `${AsyncData.GetPlaylistsVideo}-${currentPlaylistId}`);
  }
}

async function deleteVideo(event: {
  playlistId: string
  deletedVideoId: string
}) {
  await executeDeleteVideo([event.playlistId, event.deletedVideoId]);
  await executeVideos([event.playlistId, {
    page: 1,
  }, locale.value as Language], `${AsyncData.GetPlaylistsVideo}-${event.playlistId}`);
}

async function commitDeletePlaylist(id: string) {
  await executeDeletePlaylist([id]);
  await executeUserPlaylists([locale.value as Language], AsyncData.GetPlaylists);
  // Метку снимаем только после ответа: до него плейлист ещё в списке и всплыл бы обратно.
  unmarkPendingDelete(id);
}

/**
 * Удаление оптимистичное: карточка скрывается сразу, а запрос уходит, когда закроется снекбар,
 * то есть окно отмены истекло. Undo просто снимает метку, и карточка возвращается на место.
 * Удаления идут независимо друг от друга: у каждого свой id, поэтому несколько подряд
 * не путаются, а рефетч после соседнего удаления не показывает уже «удалённые» плейлисты.
 */
function deletePlaylist(event: { id: string, name?: string }) {
  markPendingDelete(event.id);

  show('deleted', {
    icon: 'trash-bin',
    highlight: event.name,
    onAction: () => unmarkPendingDelete(event.id),
    onDismiss: () => commitDeletePlaylist(event.id),
  });
}

async function saveEditPlaylist(event: {
  id: string
  playlistName: string
  playlistType: PlaylistType
}) {
  await executeRefresh([event.id, event.playlistName, event.playlistType]);
  await executeUserPlaylists([locale.value as Language], AsyncData.GetPlaylists);
  await executeVideos([event.id, {
    page: 1,
  }, locale.value as Language], `${AsyncData.GetPlaylistsVideo}-${event.id}`);
}
</script>

<style lang="scss">
.playlist-main__decorate {
  position: fixed;
  inset: 0;
  z-index: $z-index-popup;
  backdrop-filter: blur($blur-sm);
  background-color: $popup-decorate-bg;
}
</style>
