<template>
  <v-snackbar
    v-model="open"
    :timeout="-1"
    color="transparent"
    location="bottom end"
    rounded="lg"
    class="bg-transparent"
    min-width="240"
    elevation="0"
    variant="elevated"
  >
    <v-alert
      v-if="infos.length > 0"
      :model-value="true"
      :color="'#3d3dc5'"
      density="compact"
      rounded="lg"
      width="380"
      icon="download"
      class="pt-3 px-4 position-relative"
    >
      <v-btn
        icon
        variant="text"
        class="snack-close"
        @click="hideTray"
        aria-label="Close downloads"
      >
        <v-icon>close</v-icon>
      </v-btn>
      <div class="text-subtitle-1 mb-3 font-weight-bold">Downloading</div>

      <div
        v-for="(info, i) in infos"
        :key="info.Id ?? i"
        class="mb-1"
      >
        <div
          v-if="info.Title"
          class="font-weight-bold mb-3"
        >
          {{ info.Title }}
        </div>

        <v-progress-linear
          v-if="info.StatusText !== undefined"
          v-model="info.Percentage"
          :color="
            info.StatusText === 'Download Cancelled' ? '#9e9e9e' : '#8599f4'
          "
          height="16"
          rounded
          class="progress-rounded"
        >
          <strong>{{ info.StatusText }}</strong>
        </v-progress-linear>

        <v-progress-linear
          v-else-if="info.Total !== 0"
          v-model="info.Percentage"
          :color="
            info.StatusText === 'Download Cancelled' ? '#9e9e9e' : '#8599f4'
          "
          height="16"
          rounded
          class="progress-rounded"
        >
          <strong
            >{{ toFormattedBytes(info?.Completed ?? 0) }} /
            {{ toFormattedBytes(info?.Total ?? 0) }}</strong
          >
        </v-progress-linear>
      </div>
    </v-alert>
  </v-snackbar>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useDownloadStore } from '@/stores/download.store';

const dl = useDownloadStore();
const { infos } = storeToRefs(dl);

const trayVisible = ref(true);
const open = computed({
  get: () => trayVisible.value && infos.value.length > 0,
  set: (v) => {
    trayVisible.value = v;
  },
});

watch(
  () => infos.value.length,
  (len, oldLen) => {
    if ((oldLen === 0 && len > 0) || len > oldLen) trayVisible.value = true;
  },
);
function toFormattedBytes(n: number): string {
  const s = ['B', 'KB', 'MB', 'GB', 'TB'];
  let i = 0,
    v = n;
  while (v >= 1024 && i < s.length - 1) {
    v /= 1024;
    i++;
  }
  return `${v.toFixed(v >= 10 || i === 0 ? 0 : 1)} ${s[i]}`;
}

const STALE_MS = 30000;
let staleTimer: any = null;
function sweepStale() {
  const now = Date.now();
  for (const info of [...infos.value]) {
    const last = (info as any).LastUpdated ?? 0;
    if (info.StatusText === 'Paused') continue;
    if (now - last > STALE_MS) {
      const idx = infos.value.findIndex(
        (x) => (x.Id ?? (x as any).id) === (info.Id ?? (info as any).id),
      );
      if (idx >= 0) infos.value.splice(idx, 1);
    }
  }
}
onMounted(() => {
  if (!staleTimer) staleTimer = setInterval(sweepStale, 1000);
});
onUnmounted(() => {
  if (staleTimer) {
    clearInterval(staleTimer);
    staleTimer = null;
  }
});
function hideTray() {
  trayVisible.value = false;
}
</script>

<style scoped>
.progress-rounded {
  border-radius: 9999px;
  overflow: hidden;
}
.progress-rounded .v-progress-linear__determinate,
.progress-rounded .v-progress-linear__indeterminate {
  border-radius: 9999px;
}
.last\:mb-0:last-child {
  margin-bottom: 0;
}
.snack-close {
  position: absolute;
  top: 6px;
  right: 6px;
}
</style>
