<template>
  <Progress
    v-if="isProcessing"
    :size="20"
    class="processing-progress"
  />

  <div
    v-if="!isProcessing && channel"
    class="channel"
  >
    <div class="messages" @scroll.passive="messagesScroll">
      <div class="none-message" v-if="messages.length === 0">
        <span>何ということでしょう！！</span>
        <span>メッセージがありませんね！！</span>
      </div>

      <Progress
        v-if="isLoading"
        :size="10"
        class="loading-progress"
      />

      <Message
        v-for="message of messages"
        @deleteMessage="deleteMessage"
        :key="message.id"
        :message="message"
      />
    </div>

    <form>
      <textarea
        @input="resize"
        @keydown.ctrl.enter="send"
        class="message-input"
        rows="1"
        v-model="message"
        :disabled="isSending"
        :placeholder="`${channel.name}に送信`"
      />

      <Icon
        @click="send"
        :icon='isSending
          ? "line-md:loading-twotone-loop"
          : "material-symbols:send-rounded"'
      />
    </form>
  </div>
</template>

<style scoped>
.processing-progress {
  margin: auto;
}

.channel {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

.messages {
  display: flex;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  overflow: scroll;
}

.none-message {
  margin: auto;
  display: flex;
  flex-direction: column;
  text-align: center;
  gap: 0.5rem;
}

.none-message span:first-child {
  font-size: 1.25rem;
  font-weight: bold;
}

.loading-progress {
  margin: 0 auto;
}

.channel form {
  display: flex;
  gap: 0.5rem;
  padding: 1rem;
  margin-bottom: 1rem;
  border-radius: 1rem;
  background-color: var(--bg-sub-color);
}

.message-input {
  width: 100%;
  max-height: 40dvh;
  resize: none;
  border: none;
  outline: none;
  color: var(--text-color);
  background-color: var(--bg-sub-color);
}

.message-input:disabled {
  color: oklch(from var(--text-color) calc(l - 0.2) c h);
  cursor: not-allowed;
}

.channel form svg {
  cursor: pointer;
  flex-shrink: 0;
  margin-top: auto;
  width: 2rem;
  height: 2rem;
}
</style>

<script lang="ts" setup>
import { useRoute, useRouter } from "vue-router";
import { account, presentCommunity, serverInfo } from "@/lib/account";
import client from "@/lib/client";
import { createModal, createTopNotice } from "@/lib/modal";
import GoHomeError from "@/components/Modal/GoHomeError.vue";
import { onMounted, provide, ref } from "vue";
import Progress from "@/components/Progress.vue";
import { title } from "@/lib/router";
import type ApiMap from "lynqchat-js/1.0.0-alpha.0/map";
import { Icon } from "@iconify/vue";
import ErrorModal from "@/components/Modal/Error.vue";
import Message from "@/components/Message.vue";

const route = useRoute();
const router = useRouter();

if (!serverInfo.value?.success) {
  throw new Error("サーバー情報の取得に失敗しました。");
}

if (!serverInfo.value.isInitialized) {
  router.replace("/setup/initialization");
}

if (!account.value?.success) {
  switch (account.value?.error.bad) {
    case "client":
      router.replace("/signin");
      break;
    default:
      throw new Error("アカウント情報の取得に失敗しました。");
  }
}

const isProcessing = ref<boolean>(false);
const isSending = ref<boolean>(false);

const channel = ref<Extract<ApiMap["channel/list"]["response"], { channels: any }>["channels"][number]>();
const messages = ref<Extract<ApiMap["message/list"]["response"], { messages: any }>["messages"]>([]);

const message = ref<string>("");

const isInsideRange = ref(false);
const isLoading = ref(false);
const messagesScroll = async (event: any) => {
  const handleSize = 200;

  const isCurrentInside = event.target.scrollTop < handleSize;
  if (isCurrentInside && !isInsideRange.value && !isLoading.value) {
    isLoading.value = true;
    await new Promise<void>(resolve => setTimeout(resolve, 0));
    document.querySelector(".messages")!.scrollTop = 0;

    if (channel.value === undefined) {
      isLoading.value = false;
      createModal({
        component: ErrorModal,
        onClose: () => isSending.value = false,
        props: {
          error: "チャンネルの情報がありません。",
          canClose: true,
        },
      });
      return;
    }

    const messagesRes = await client.value.request("message/list", {
      channel: channel.value.id,
      until: messages.value[0]?.id,
    });

    if (!messagesRes.success) {
      isLoading.value = false;
      createModal({
        component: ErrorModal,
        onClose: async () => await router.push("/"),
        props: {
          error: "メッセージが取得できませんでした。",
          canClose: true,
        },
      });
      return;
    }

    messages.value = messagesRes.messages.toReversed().concat(messages.value);
    isLoading.value = false;
  }

  isInsideRange.value = isCurrentInside;
}

const resize = (event: any) => {
  event.target.style.height = "auto";
  event.target.style.height = event.target.scrollHeight + 'px';
}

const send = async (e: Event) => {
  e.preventDefault();
  isSending.value = true;

  if (channel.value === undefined) {
    createModal({
      component: ErrorModal,
      onClose: () => isSending.value = false,
      props: {
        error: "チャンネルの情報がありません。",
        canClose: true,
      },
    });
    return;
  }

  if (message.value.trim().length > 4096) {
    isSending.value = false;
    createTopNotice({
      props: {
        icon: "material-symbols:warning-rounded",
        iconColor: "var(--warn-color)",
        error: "メッセージは4096文字までである必要があります。",
      },
    });
    return;
  }

  if (message.value.trim().length === 0) {
    isSending.value = false;
    createTopNotice({
      props: {
        icon: "material-symbols:warning-rounded",
        iconColor: "var(--warn-color)",
        message: "メッセージを空にすることは出来ません。",
      },
    });
    return;
  }

  const channelId = route.params.channelId;
  if (typeof channelId !== "string") {
    isSending.value = false;
    createModal({
      component: ErrorModal,
      props: {
        error: "チャンネルが取得できませんでした。",
        canClose: true,
      },
    });
    return;
  }

  if (!account.value?.success) {
    isSending.value = false;
    createModal({
      component: ErrorModal,
      props: {
        error: "アカウント情報の取得に失敗しました。",
        canClose: true,
      },
    });
    return;
  }
  
  const sendRes = await client.value.request("message/send", {
    message: message.value,
    channel: channel.value.id,
  });

  if (!sendRes.success) {
    isSending.value = false;
    createModal({
      component: ErrorModal,
      props: {
        error: "メッセージの送信に失敗しました。",
        canClose: true,
      },
    });
    return;
  }

  messages.value = [...messages.value, {
    id: sendRes.id,
    message: message.value,
    channel: channelId,
    createdAt: new Date().toUTCString(),
    createdBy: {
      ...account.value,
    },
  }];

  message.value = "";
  const inputElem = document.querySelector(".message-input") as HTMLTextAreaElement;
  inputElem.style.height = "auto";
  isSending.value = false;

  await new Promise<void>(resolve => setTimeout(resolve, 0));
  const messagesElem = document.querySelector(".messages")!;
  messagesElem.scrollTop = messagesElem.scrollHeight;
}

const deleteMessage = (data: { id: string }) => {
  const index = messages.value.findIndex(msg => msg.id === data.id);
  if (index !== -1)
    messages.value = messages.value.toSpliced(index, 1);
}

const now = ref(new Date());
onMounted(() => {
  setInterval(() =>
    now.value = new Date()
  , 1000);
});
provide("now", now);

(async () => {
  isProcessing.value = true;

  if (!serverInfo.value?.success) {
    throw new Error("サーバー情報の取得に失敗しました。");
  }

  const channelId = route.params.channelId;
  if (typeof channelId !== "string") {
    isProcessing.value = false;
    createModal({
      component: GoHomeError,
      onClose: async () => await router.push("/"),
      props: {
        error: "不正なアクセスです。",
      },
    });
    return;
  }

  if (presentCommunity.value === undefined) {
    isProcessing.value = false;
    createModal({
      component: GoHomeError,
      onClose: async () => await router.push("/"),
      props: {
        error: "不正なアクセスです。",
      },
    });
    return;
  }

  const channelRes = await client.value.request("channel/get", {
    id: channelId,
  });

  if (!channelRes.success) {
    isProcessing.value = false;
    createModal({
      component: GoHomeError,
      onClose: async () => await router.push("/"),
      props: {
        error: "チャンネルが取得できませんでした。",
      },
    });
    return;
  }

  channel.value = channelRes.channel;

  document.title = `${channelRes.channel.name} - ${presentCommunity.value.name} | ${serverInfo.value.name}`;
  title.value = `${channelRes.channel.name} - ${presentCommunity.value.name}`;

  const messagesRes = await client.value.request("message/list", {
    channel: channel.value.id,
  });

  if (!messagesRes.success) {
    isProcessing.value = false;
    createModal({
      component: GoHomeError,
      onClose: async () => await router.push("/"),
      props: {
        error: "メッセージが取得できませんでした。",
      },
    });
    return;
  }

  messages.value = messages.value.concat(messagesRes.messages).reverse();
  isProcessing.value = false;

  await new Promise<void>(resolve => setTimeout(resolve, 0));
  const messagesElem = document.querySelector(".messages")!;
  messagesElem.scrollTop = messagesElem.scrollHeight;
})();
</script>