<template>
  <div
    :class='[
      "ueuse",
      "flex", "w-full", "h-fit",
      "rounded-lg", "gap-2",
    ]'
    v-if="authState.isSignedIn"
    ref="ueuseElement"
  >
    <RouterLink class="shrink-0 h-fit rounded-full" :to="`/@${data.account.userid}`">
      <img
        :class='[
          "w-12", "h-12", "rounded-full", "cursor-pointer",
          "block", "shrink-0", "object-cover", "ueuse-user-icon"
        ]'
        :src="data.account.user_icon"
        v-if="data.text !== ''"
      />
    </RouterLink>
    
    <div class="flex flex-col grow gap-1 min-w-0">
      <div class="flex gap-1 ueuse-detail">
        <RouterLink
          :to="`/@${data.account.userid}`"
          class="flex gap-1 ueuse-detail-account truncate"
        >
          <img
            :class='[
              "block", "w-6", "h-6", "rounded-full", "mr-1",
              "shrink-0", "object-cover", "ueuse-user-icon"
            ]'
            :src="data.account.user_icon"
            v-if="isReuse && data.text === ''"
          />
          
          <span class="font-bold ueuse-user-name truncate">
            {{ data.account.username }}
          </span>
          <span class="ueuse-user-id text-(--text-color)/85 truncate">
            @{{ data.account.userid }}
          </span>

          <Icon
            icon="material-symbols:robot-2-rounded"
            class="w-4 h-4 shrink-0 m-auto opacity-70"
            v-if="data.account.is_bot"
          />

          <Icon
            icon="material-symbols:check-circle-rounded"
            class="w-4 h-4 shrink-0 m-auto text-(--accent)"
            v-if="/*Botかどうかを毎回取得してたらきりない*/false"
          />
        </RouterLink>
       
        <Icon
          icon="material-symbols:repeat-rounded"
          class="w-4 h-4 shrink-0 my-auto text-(--accent)"
          v-if="isReuse && data.text === ''"
        />
        
        <div class="ml-auto w-fit shrink-0">
          {{ parsedDate }}
        </div>
      </div>
      
      <div
        :class='[
          "flex", "flex-col",
          "bg-(--accent)", "text-(--accent-in-text)",
          "rounded-lg", "p-2", "gap-1",
          "ueuse-nsfw",
        ]'
        v-if="data.nsfw"
      >
        <div class="flex shrink items-center w-full gap-1">
          <Icon
            class="my-auto break-all"
            icon="material-symbols:visibility-off-rounded"
          />
          NSFWユーズ
          
          <Button
            @click="visibility = !visibility"
            class="px-2! ml-auto text-sm border border-(--text-color)"
          >
            {{ visibility
              ? "非表示"
              : "表示"
            }}
          </Button>
        </div>
        
        <div class="ueuse-nsfw-detail">
          {{ detail }}
        </div>
      </div>
      
      <div
        class="flex flex-col gap-1 ueuse-content"
        v-if="visibility"
      >
        <p class="ueuse-text break-all" v-if="parsedText">
          <template v-for="(node, i) in parsedText" :key="i">
            <component v-if="typeof node !== 'string'" :is="node" />
            <span v-else>{{ node }}</span>
          </template>
        </p>
        
        <div
          class="grid gap-1 ueuse-media"
          :class="mediaGridClass"
          v-if="medias.length > 0"
        >
          <template
            v-for="(media, i) in medias"
            :key="media"
          >
            <Media
              v-if="
                i < 2 ||
                (medias.length >= 4 &&
                i < 4)
              "
              :src="media"
              class="col-span-1 aspect-4/3"
              @click="showLgMedia(media)"
            />
            <Media
              v-else
              :src="media"
              class="col-span-2 aspect-2/1"
              @click="showLgMedia(media)"
            />
          </template>
        </div>
        
        <div
          :class='[
            "flex", "flex-col", "p-2", "rounded-lg", "gap-1.5",
            "border", "border-(--border-color)", "bg-(--bg-color)/70", "ueuse-abi",
          ]'
          v-if="data.abi !== '' && data.abidatetime !== ''"
        >
          <span :class='[
            "bg-(--accent)", "text-(--accent-in-text)",
            "px-2", "py-1.5", "rounded-md", "font-bold", "flex",
          ]'>
            <Icon
              class="my-auto mr-1 w-5 h-5"
              icon="material-symbols:add-notes-rounded"
            />
            追記
          </span>
        
          <p class="ueuse-abi-content break-all" v-if="parsedAbiText">
            <template v-for="(node, i) in parsedAbiText" :key="i">
              <component v-if="typeof node !== 'string'" :is="node" />
              <span v-else>{{ node }}</span>
            </template>
          </p>
        
          <span class="text-sm text-(--text-color)/70">
            {{ parsedAbiDate }}
          </span>
        </div>
        
        <Ueuse
          :data="reuseData"
          :lgMedia="lgMedia"
          :depth="depth + 1"
          v-if="isReuse && reuseData"
          :class='[
            "mt-2",
            {
              "border": data.text !== "",
              "border-(--border-color)": data.text !== "",
              "p-2": data.text !== "",
            },
          ]'
        />
      </div>
      
      <div
        class="mt-2 flex flex-wrap gap-3 ueuse-operation"
        v-if="data.text !== ''"
      >
        <Operation
          @click="likeUpdate(data.uniqid)"
          name="like"
          icon="material-symbols:favorite-outline-rounded"
          usedIcon="material-symbols:favorite-rounded"
          :isUse="data.favorite.includes(authState.me.userid)"
          :data="data.favorite_cnt"
        />
        <Operation
          name="reuse"
          icon="material-symbols:repeat-rounded"
          :data="data.reuse_cnt"
        />
        <RouterLink
          class="rounded-2xl"
          :to="`/ueuse/${data.uniqid}`"
        >
          <Operation
            name="reply"
            icon="material-symbols:reply-rounded"
            :data="data.reply_cnt"
          />
        </RouterLink>
        <Operation
          name="more"
          icon="material-symbols:more-horiz"
          class="ml-auto"
          @click="showMoreMenu"
        />
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import type ueuseModule from "better-uwuzu-sdk/types/1.6.8/types/modules/ueuse";
import { DateParse, parseTextToNodes } from "@/lib/parser";
import { RouterLink } from "vue-router";
import Button from "@/components/Button.vue";
import Media from "@/components/Media/index.vue";
import Ueuse from "@/components/Ueuse/index.vue";
import Operation from "@/components/Ueuse/Operation.vue";
import { Icon } from "@iconify/vue";
import uwuzu from "better-uwuzu-sdk";
import type ApiMap from "better-uwuzu-sdk/types/1.6.8/map";
import Parser from "better-uwuzu-sdk/1.6.8/parser";
import {
  ref, inject, computed, watch,
  onMounted, onBeforeUnmount,
  type Reactive, type Ref,  type Component,
} from "vue";
import { authState } from "@/lib/account";
import waitUntil from "@/lib/wait";
import { ueuseMoreMenu } from "@/components/Ueuse/More.vue";

const props = defineProps<{
  data: ueuseModule;
  lgMedia: Reactive<{
    src: string;
  }>;
  depth?: number;
}>();

const depth = props.depth ?? 0;
const MAX_DEPTH = ref<number>(1);

const apiClient = ref<uwuzu<ApiMap>>();

const visibility = ref<boolean>(!props.data.nsfw);
const isReuse = props.data.reuseid !== "" && depth < MAX_DEPTH.value;
const reuseData = ref<ueuseModule>();

const now = inject<Ref<Date>>("now")!;

type ParsedNodes = (string | Component)[];

const parsedText = ref<ParsedNodes | null>(null);
const parsedAbiText = ref<ParsedNodes | null>(null);

const ueuseElement = ref<HTMLElement | null>(null);
const parsedDate = ref("");
const parsedAbiDate = ref("");
let observer: IntersectionObserver;

onMounted(() => {
  if (!ueuseElement.value) return;

  observer = new IntersectionObserver((entries) => {
    for (const entry of entries) {
      if (!entry.isIntersecting)
        continue;

      parsedDate.value ||= DateParse(props.data.datetime, now.value);

      if (props.data.abidatetime) {
        parsedAbiDate.value ||= DateParse(props.data.abidatetime, now.value);
      }

      if (
        visibility.value &&
        props.data.text &&
        !parsedText.value
      ) {
        parsedText.value = parseTextToNodes(props.data.text);
      }

      if (
        visibility.value &&
        props.data.abi &&
        props.data.abidatetime &&
        !parsedAbiText.value
      ) {
        parsedAbiText.value = parseTextToNodes(props.data.abi);
      }

      observer.unobserve(entry.target);
    }
  }, {
    rootMargin: "100px 0px",
  });

  observer.observe(ueuseElement.value);
});

onBeforeUnmount(() => {
  observer?.disconnect();
});

watch(visibility, (v) => {
  if (!v)
    return;

  if (props.data.text !== "") {
    parsedText.value ||= parseTextToNodes(props.data.text);
  }

  if (props.data.abi !== "") {
    parsedAbiText.value ||= parseTextToNodes(props.data.abi);
  }
});

const medias = props.data.media.photo.concat(props.data.media.video);

const mediaGridClass = computed(() => {
  switch (medias.length) {
    case 1:
      return "grid-cols-1";
    default:
      return "grid-cols-2";
  }
});

const detail = computed(() => {
  const result: string[] = [];
  
  if (props.data.text.trim() !== "" && props.data.reuseid === "") {
    result.push(`${props.data.text.trim().length}文字の本文`);
  }
  
  if (medias.length > 0) {
    result.push(`${medias.length}枚のメディア`)
  }
  
  if (props.data.abi.trim() !== "") {
    result.push(`${props.data.abi.trim().length}文字の追記`);
  }
  
  if (props.data.replyid !== "") {
    result.push("返信");
  }
  
  if (props.data.reuseid !== "") {
    if (props.data.text === "") {
      result.push("リユーズ");
    } else {
      result.push(`${props.data.text.trim().length}文字の引用`);
    }
  }
  
  return result.join("・");
});

const showLgMedia = (src: string) => {
  const ext = src.slice(src.lastIndexOf(".") + 1).toLowerCase();

  const isPhoto = () => {
    const videoExts = ["mp4", "mpeg", "mpg", "webm", "avi"];
    return !videoExts.includes(ext);
  };

  if (!isPhoto())
    return;

  props.lgMedia.src = src;
}

const likeUpdate = async (uniqid: string) => {
  await waitUntil(() => apiClient.value !== undefined, 100);
  if (!apiClient.value) return false;
  if (!authState.isSignedIn) return false;

  MAX_DEPTH.value = authState.reuse_maxdepth;

  try {
    const chg = await apiClient.value.request("favorite/change", {
      uniqid: uniqid,
    });

    if (chg.success) {
      props.data.favorite = chg.favorite_list;
      props.data.favorite_cnt = chg.favorite_list.length - 1;
    }

    return chg.success;
  } catch {
    return false;
  }
}

const showMoreMenu = (event: MouseEvent) => {
  if (!ueuseMoreMenu.visibility) {
    ueuseMoreMenu.uniqid = props.data.uniqid;
    ueuseMoreMenu.position = event.pageY;
    ueuseMoreMenu.visibility = true;
  }
}

(async () => {
  if (!authState.isSignedIn)
    return;
  
  apiClient.value = new uwuzu<ApiMap>({
    origin: authState.origin,
    parser: Parser,
  });
  apiClient.value.token = authState.token;
  
  if (isReuse) {
    const ueuse = await apiClient.value.request("ueuse/get", {
      uniqid: props.data.reuseid,
    });
    
    if (!ueuse.success) {
      throw new Error(ueuse.error_code);
    }
    
    reuseData.value = ueuse.data[0];
  }
})();
</script>
