<template>
  <component
    :is="rootComponent"
    :class="isIncoming ? 'messageIn-thread' : 'messageOut-thread'"
    ref="root"
    v-bind="{ ...$props, ...$attrs }"
    @vue:mounted="
      (item) => {
        emits('componentMounted', item);
      }
    "
  >
    <component
      :is="textComponent"
      v-bind="{ ...$props, ...$attrs }"
      :originalEvent="event"
      ref="exportedEvent"
      @vue:mounted="
        (item) => {
          emits('componentMounted', item);
        }
      "
    />
    <component
      v-for="item in items"
      :is="$props.componentFn(item.event, true)"
      v-bind="{ ...$props, ...$attrs }"
      :originalEvent="item.event"
      :key="item.event.getId()"
      ref="exportedEvent"
      @vue:mounted="
        (item) => {
          emits('componentMounted', item);
        }
      "
    />
  </component>
</template>

<script setup lang="ts">
import MessageIncoming from "./MessageIncoming.vue";
import MessageOutgoing from "./MessageOutgoing.vue";
import MessageIncomingText from "../MessageIncomingText.vue";
import MessageOutgoingText from "../MessageOutgoingText.vue";
import { MessageProps, useMessage } from "./useMessage";
import util from "@/plugins/utils";
import { computed, inject, onBeforeUnmount, ref, Ref, watch } from "vue";
import { EventAttachment } from "../../../models/eventAttachment";
import { useI18n } from "vue-i18n";
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk";

const { t } = useI18n();
const $matrix: any = inject("globalMatrix");

const emits = defineEmits<
  { (event: "layout-change", value: { element: Element | undefined; action: () => void }): void } & {
    (event: "componentMounted", value: any): void;
  }
>();

const items: Ref<EventAttachment[]> = ref([]);
const props = defineProps<MessageProps>();

let beforeExportPromiseResolve: ((value: boolean) => void) | undefined = undefined;
let beforeExportPromiseReject: ((value: boolean) => void) | undefined = undefined;
const beforeExportPromise = new Promise((resolve, reject) => {
  beforeExportPromiseResolve = resolve;
  beforeExportPromiseReject = reject;
});

const processThread = () => {
  if (!event.value?.isRedacted()) {
    _processThread();
  }
};

const { event, thread, isIncoming, messageText } = useMessage($matrix, t, props, undefined, processThread);

const rootComponent = computed(() => {
  return isIncoming.value ? MessageIncoming : MessageOutgoing;
});

const textComponent = computed(() => {
  if (messageText.value && messageText.value.length > 0) {
    return isIncoming.value ? MessageIncomingText : MessageOutgoingText;
  }
  return undefined;
});

const onRelationsCreated = () => {
  if (event.value) {
    thread.value = props.timelineSet.relations.getChildEventsForEvent(
      event.value.getId() ?? "",
      util.threadMessageType(),
      "m.room.message"
    );
    event.value.off(MatrixEventEvent.RelationsCreated, onRelationsCreated);
  }
};

watch(
  event,
  () => {
    if (event.value) {
      if (thread.value === undefined) {
        thread.value = props.timelineSet.relations.getChildEventsForEvent(
          event.value.getId() ?? "",
          util.threadMessageType(),
          "m.room.message"
        );
      }
      if (!thread.value) {
        event.value.on(MatrixEventEvent.RelationsCreated, onRelationsCreated);
      }
    }
  },
  { immediate: true }
);

onBeforeUnmount(() => {
  event.value?.off(MatrixEventEvent.RelationsCreated, onRelationsCreated);
});

const _processThread = () => {
  const eventItems = props.timelineSet.relations
    .getAllChildEventsForEvent(event.value?.getId() ?? "")
    .filter((e: MatrixEvent) => !e.isRedacted() && util.downloadableTypes().includes(e.getContent().msgtype));
  items.value = eventItems.map((e: MatrixEvent) => {
    let ea = $matrix.attachmentManager.getEventAttachment(e);
    return ea;
  });
  if (beforeExportPromiseResolve) {
    beforeExportPromiseResolve(true);
  }
};

const beforeExport = () => {
  return beforeExportPromise;
};

defineExpose({
  beforeExport,
});
</script>
<style lang="scss">
@use "@/assets/css/chat.scss" as *;
</style>
