<template>
  <div ref="thumbnailRef" style="width: 100%; height: 100%">
    <v-responsive v-if="isVideo && (source || poster)" :class="{ 'thumbnail-item': true, preview: previewOnly }">
      <video :src="source" :poster="poster" :controls="!previewOnly" class="w-100 h-100">
        {{ $t("fallbacks.video_file") }}
      </video>
    </v-responsive>
    <ImageWithProgress
      v-else-if="isImage"
      :aspect-ratio="previewOnly ? 16 / 9 : undefined"
      :class="{ 'thumbnail-item': true, preview: previewOnly }"
      :src="source"
      :contain="!previewOnly"
      :cover="previewOnly"
      :loadingProgress="loadingProgress"
    />
    <div v-else :class="{ 'thumbnail-item': true, preview: previewOnly, 'file-item': true, 'pdf-file': isPDF }">
      <div class="pdf-container" v-if="showInlinePDF && isPDF">
        <div
          v-for="(pageNum, index) in pageNums"
          :key="pageNum"
          :ref="(el) => { if ((el != null && el as Element)) pageRefs[index] = el as Element }"
        >
          <vue-pdf-embed
            v-if="pageVisibility[pageNum]"
            :source="doc"
            :page="pageNum"
          />
        </div>
      </div>
      <template v-else>
        <v-icon :class="fileTypeIconClass">{{ fileTypeIcon }}</v-icon>
        <div class="file-name">{{ $$sanitize(fileName) }}</div>
        <div class="file-size">{{ fileSize }}</div>
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
import { singleOrDoubleTapRecognizer } from "../../plugins/touch";
import { computed, inject, onBeforeUnmount, onMounted, Ref, ref, useTemplateRef, watch, nextTick } from "vue";
import { EventAttachment } from "../../models/eventAttachment";
import { useThumbnail } from "../messages/composition/useThumbnail";
import { Attachment } from "../../models/attachment";
import ImageWithProgress from "../ImageWithProgress.vue";
import VuePdfEmbed, { useVuePdfEmbed } from 'vue-pdf-embed';
import { Source } from 'vue-pdf-embed/types';

function isEventAttachment(source: EventAttachment | Attachment | undefined): source is EventAttachment {
    return (source as EventAttachment)?.event !== undefined;
}

function isAttachment(source: EventAttachment | Attachment | undefined): source is Attachment {
    return (source as Attachment)?.file !== undefined;
}

const $$sanitize: any = inject("globalSanitize");

const thumbnailRef = useTemplateRef("thumbnailRef");

interface ThumbnailProps {
  item?: EventAttachment | Attachment;
  previewOnly?: boolean;
  showInlinePDF?: boolean;
}

type ThumbnailEmits = {
  (event: "itemclick", value: { item: EventAttachment | Attachment }): void;
};

const props = defineProps<ThumbnailProps>();
const { item, previewOnly = false, showInlinePDF = false } = props;
const emits = defineEmits<ThumbnailEmits>();

let { isVideo, isImage, fileTypeIcon, fileTypeIconClass, fileName, fileSize, isPDF } = useThumbnail(isEventAttachment(props.item) ? props.item.event : isAttachment(props.item) ? props.item.file : undefined);

const fileURL: Ref<string | undefined> = ref(undefined);
const source: Ref<string | undefined> = ref(undefined);
const poster: Ref<string | undefined> = ref(undefined);

const updateSource = () => {
  if (isEventAttachment(props.item)) {
    const eventAttachment = props.item;
    if (eventAttachment.src) {
      source.value = eventAttachment.src;
    } else if (previewOnly) {
      eventAttachment.loadThumbnail().then((url) => {
        source.value = url.data;
      })
    } else if (isImage.value) {
      eventAttachment.loadSrc().then((url) => {
        source.value = url.data;
      })
    } else if (isVideo.value) {
      eventAttachment.loadSrc().then((url) => {
        source.value = url.data;
      })
    }
  } else if (isAttachment(props.item)) {
    const attachment = props.item;
    if (fileURL.value) {
      URL.revokeObjectURL(fileURL.value);
    }
    fileURL.value = URL.createObjectURL(attachment.file);
    source.value = fileURL.value;
  } else {
    source.value = undefined;
  }
};

const updatePoster = () => {
  if (isEventAttachment(props.item) && isVideo.value) {
    const eventAttachment = props.item;
    if (eventAttachment.thumbnail) {
      poster.value = eventAttachment.thumbnail;
    } else {
      eventAttachment
        .loadThumbnail()
        .then((url) => {
          poster.value = url.data;
        })
        .catch(() => {
          poster.value = undefined;
        });
    }
  }
};

updateSource();
updatePoster();


watch(props, (props: ThumbnailProps) => {
  const updates = useThumbnail(isEventAttachment(props.item) ? props.item.event : isAttachment(props.item) ? props.item.file : undefined);
  isVideo.value = updates.isVideo.value;
  isImage.value = updates.isImage.value;
  isPDF.value = updates.isPDF.value;
  fileTypeIcon = updates.fileTypeIcon;
  fileTypeIconClass = updates.fileTypeIconClass;
  fileName = updates.fileName;
  fileSize = updates.fileSize;
  updateSource();
  updatePoster();
});

const pageRefs: Ref<(Element)[]> = ref([]);
const pageVisibility: Ref<{ [n: number]: boolean }> = ref({});
let pageIntersectionObserver: IntersectionObserver | null = null;
const pdfSource: Ref<Source | null> = ref(null);

const { doc } = useVuePdfEmbed({
  source: pdfSource,
});

const pageNums = computed(() =>
  doc.value ? [...Array(doc.value.numPages + 1).keys()].slice(1) : []
);

const resetPageIntersectionObserver = () => {
  pageIntersectionObserver?.disconnect()
  pageIntersectionObserver = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const index = pageRefs.value.findIndex((el) => el === entry.target)
        if (index !== -1) {
          const pageNum = pageNums.value[index]
          pageVisibility.value[pageNum] = true
        }
      }
    })
  })

  pageRefs.value.forEach((element) => {
    if (element && pageIntersectionObserver) pageIntersectionObserver.observe(element)
  })
};

watch(pageNums, (newPageNums) => {
  pageVisibility.value = { [newPageNums[0]]: true }
  pageRefs.value = []
  nextTick(resetPageIntersectionObserver)
});

watch(source, (newSource) => {
    if (isPDF.value) {
      pdfSource.value = newSource;
    }
  }
);

const loadingProgress = computed(() => {
  if (isEventAttachment(item)) {
    const eventAttachment = item;
    return previewOnly ? eventAttachment.thumbnailProgress : eventAttachment.srcProgress;
  }
  return -1;
});

onMounted(() => {
  if (thumbnailRef.value && item) {
    const hammerInstance = singleOrDoubleTapRecognizer(thumbnailRef.value);
    hammerInstance.on("singletap doubletap", (ev: any) => {
      if (ev.type === "singletap") {
        emits("itemclick", { item: item });
      }
    });
  }
  if (!previewOnly && item && isEventAttachment(item)) {
    item.loadSrc();
  }
});

onBeforeUnmount(() => {
  if (fileURL.value) {
    URL.revokeObjectURL(fileURL.value);
    fileURL.value = undefined;
  }
  pageIntersectionObserver?.disconnect()
});
</script>

<style lang="scss">
@use "@/assets/css/chat.scss" as *;

.thumbnail-item {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;

  &.pdf-file {
    overflow-y: auto;
  }
}

.file-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  flex-direction: column;
  padding: 20px;
  .v-icon {
    margin-bottom: 10px;
    color: currentColor;
  }
}

.pdf-container {
  padding: 24px 16px;
  margin-top: 200px;

  & > * {
    margin: 30px auto 0 auto;
    min-height: 400px;
  }

  .vue-pdf-embed {
    border: 1px solid #ccc;
    width: 100%;
  }
}
</style>
