<script lang="ts">
import { mediaListCollection, Type, type Media } from '$lib/Data/AniList/media';
import type { AniListAuthorisation } from '$lib/Data/AniList/identity';
import { onDestroy, onMount } from 'svelte';
import anime from '$stores/anime';
import settings from '$stores/settings';
import lastPruneTimes from '$stores/lastPruneTimes';
import AnimeList from './AnimeListTemplate.svelte';
import type { SubsPlease } from '$lib/Media/Anime/Airing/Subtitled/subsPlease';
import { injectAiringTime } from '$lib/Media/Anime/Airing/Subtitled/match';
import { hasDueEpisodes, hasNoAiredEpisodes } from '$lib/Media/Anime/Airing/classify';
import { addNotification } from '$lib/Notification/store';
import locale from '$stores/locale';
import identity from '$stores/identity';

export let user: AniListAuthorisation;
let animeLists: Promise<Media[]>;
let startTime: number;
let endTime: number;
let keyCacher: ReturnType<typeof setInterval> | undefined;
let keyCacheMinutes = -1;

const restartKeyCacher = (cacheMinutes: number) => {
  if (keyCacher) clearInterval(keyCacher);

  keyCacheMinutes = cacheMinutes;
  keyCacher = setInterval(
    () => {
      startTime = performance.now();
      endTime = -1;
      animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
        forcePrune: true,
        addNotification
      });
    },
    cacheMinutes * 1000 * 60
  );
};

onMount(async () => {
  restartKeyCacher($settings.cacheMinutes);

  startTime = performance.now();
  animeLists = mediaListCollection(user, $identity, Type.Anime, $anime, $lastPruneTimes.anime, {
    addNotification
  });
});

$: if (keyCacher && keyCacheMinutes !== $settings.cacheMinutes)
  restartKeyCacher($settings.cacheMinutes);

onDestroy(() => {
  if (keyCacher) clearInterval(keyCacher);
});

const cleanMedia = (anime: Media[], displayUnresolved: boolean, subsPlease: SubsPlease | null) => {
  if (anime === undefined) return [];

  let dueAnime = anime
    .map((media) => injectAiringTime(media, subsPlease))
    .filter(
      // Releasing media
      (media: Media) =>
        media.status === 'RELEASING' &&
        (media.mediaListEntry || { status: 'DROPPED' }).status !==
          ($settings.displayPausedMedia ? '' : 'PAUSED') &&
        (media.mediaListEntry || { progress: 0 }).progress >=
          ($settings.displayNotStarted === true ? 0 : 1) &&
        (media.mediaListEntry || { status: 'DROPPED' }).status !== 'DROPPED'
    )
    .filter((media: Media) =>
      // Outdated media
      hasDueEpisodes(media)
    )
    .map((media: Media) => {
      if (hasNoAiredEpisodes(media)) media.nextAiringEpisode = { episode: -1 };

      return media;
    });

  if (!displayUnresolved)
    dueAnime = dueAnime.filter((media: Media) => media.nextAiringEpisode?.episode !== -1);

  dueAnime.sort((a: Media, b: Media) => {
    switch ($settings.displayAnimeSort) {
      case 'difference': {
        const difference = (anime: Media) =>
          (anime.nextAiringEpisode?.episode === -1
            ? 99999
            : anime.nextAiringEpisode?.episode || -1) -
          (anime.mediaListEntry || { progress: 0 }).progress;

        return difference(a) - difference(b);
      }

      case 'end_date':
        return (
          new Date(a.endDate.year, a.endDate.month - 1).getTime() -
          new Date(b.endDate.year, b.endDate.month - 1).getTime()
        );

      case 'start_date':
        return (
          new Date(a.startDate.year, a.startDate.month - 1).getTime() -
          new Date(b.startDate.year, b.startDate.month - 1).getTime()
        );

      case 'time_remaining':
        return (a.nextAiringEpisode?.airingAt || 9999) - (b.nextAiringEpisode?.airingAt || 9999);

      default:
        return 0;
    }
  });

  if (!endTime || endTime === -1) endTime = performance.now() - startTime;

  return dueAnime;
};
</script>

<AnimeList {endTime} {cleanMedia} bind:animeLists {user} title={$locale().lists.due.episodes} />
