<template>
	<div class="memorietTimeline">
		<MkLoading v-if="loading && items.length === 0" />
		<div v-else-if="items.length === 0" class="_fullinfo">
			<div>表示できるMemorietはありません</div>
		</div>
		<div v-else class="items">
			<article v-for="item in items" :key="item.id" class="item">
				<div class="meta">
					<span>
						<i class="ph-timer ph-bold"></i>
						{{ item.expiresAt ? `${formatDate(item.expiresAt)}に削除` : "無期限公開" }}
					</span>
					<button v-if="item.viewerCount != null" class="_button footprintButton" @click="toggleViewers(item)">
						<i class="ph-footprints ph-bold"></i>
						足跡 {{ item.viewerCount }}
					</button>
				</div>
				<div class="storyPreview">
					<img v-if="itemImage(item)" :src="itemImage(item)?.url" :alt="itemImage(item)?.name" />
					<div v-else class="previewEmpty">画像なし</div>
					<div
						v-for="layer in item.textLayers"
						:key="layer.id"
						class="storyText"
						:style="layerStyle(layer)"
					>
						{{ layer.text }}
					</div>
				</div>
				<div v-if="item.viewers" class="viewerList">
					<div v-if="item.viewers.length === 0">まだ閲覧者はいません</div>
					<template v-else>
						<div v-for="view in item.viewers" :key="`${item.id}-${view.user.id}`" class="viewerRow">
							<MkAvatar :user="view.user" class="avatar" />
							<span class="viewerName">{{ view.user.name || view.user.username }}</span>
							<span class="viewedAt">{{ formatDate(view.viewedAt) }}</span>
						</div>
					</template>
				</div>
				<MkNote :note="item.note" />
			</article>
		</div>
		<div v-if="items.length > 0" class="actions">
			<MkButton :disabled="loading" @click="loadMore">
				<i class="ph-arrow-down ph-bold"></i>
				さらに読み込む
			</MkButton>
		</div>
	</div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import * as misskey from "iceshrimp-sdk";
import MkButton from "@/components/MkButton.vue";
import MkNote from "@/components/MkNote.vue";
import MkAvatar from "@/components/global/MkAvatar.vue";
import * as os from "@/os";

type MemorietTextLayer = {
	id: string;
	text: string;
	color: string;
	backgroundColor: string | null;
	backgroundWidth: number | null;
	backgroundHeight: number | null;
	fontSize: number;
	x: number;
	y: number;
	rotate: number;
};

type MemorietView = {
	viewedAt: string;
	user: misskey.entities.User;
};

type MemorietItem = {
	id: string;
	expiresAt: string | null;
	textLayers: MemorietTextLayer[];
	viewerCount: number | null;
	viewers?: MemorietView[];
	note: misskey.entities.Note;
};

const items = ref<MemorietItem[]>([]);
const loading = ref(false);
const limit = 20;

function layerStyle(layer: MemorietTextLayer) {
	const backgroundColor = layer.backgroundColor;
	return {
		color: layer.color,
		backgroundColor: backgroundColor ?? "transparent",
		fontSize: `${layer.fontSize}px`,
		left: `${layer.x}%`,
		top: `${layer.y}%`,
		transform: `translate(-50%, -50%) rotate(${layer.rotate}deg)`,
		padding: backgroundColor == null ? "0" : "0.25em 0.45em",
		width: layer.backgroundWidth != null && layer.backgroundWidth > 0 ? `${layer.backgroundWidth}%` : "auto",
		height: layer.backgroundHeight != null && layer.backgroundHeight > 0 ? `${layer.backgroundHeight}%` : "auto",
	};
}

function itemImage(item: MemorietItem): misskey.entities.DriveFile | null {
	return item.note.files?.find((file) => file.type?.startsWith("image/")) ?? null;
}

async function load(offset = 0) {
	loading.value = true;
	try {
		const next = (await (os.api as any)("memoriet/list", { limit, offset })) as MemorietItem[];
		items.value = offset === 0 ? next : [...items.value, ...next];
	} finally {
		loading.value = false;
	}
}

async function loadMore() {
	await load(items.value.length);
}

async function toggleViewers(item: MemorietItem) {
	if (item.viewers) {
		item.viewers = undefined;
		return;
	}
	item.viewers = (await (os.api as any)("memoriet/viewers", { memorietId: item.id, limit: 100 })) as MemorietView[];
}

function formatDate(value: string): string {
	return new Date(value).toLocaleString();
}

load();
</script>

<style lang="scss" scoped>
.memorietTimeline {
	display: grid;
	gap: 16px;
}

.items {
	display: grid;
	gap: 18px;
}

.item {
	display: grid;
	gap: 10px;
	padding: 14px;
	border-radius: 8px;
	background: var(--panel);
	border: solid 1px var(--divider);
}

.meta,
.actions {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 10px;
	flex-wrap: wrap;
	color: var(--fgTransparentWeak);
	font-size: 0.9em;
}

.meta span,
.footprintButton {
	display: inline-flex;
	align-items: center;
	gap: 6px;
}

.footprintButton {
	min-height: 34px;
	padding: 0 10px;
	border-radius: 8px;
	background: var(--buttonBg);
	color: var(--fg);
}

.storyPreview {
	position: relative;
	overflow: hidden;
	aspect-ratio: 9 / 16;
	width: min(100%, 420px);
	border-radius: 8px;
	background: var(--bg);
	border: solid 1px var(--divider);
}

.storyPreview img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.previewEmpty {
	display: grid;
	place-items: center;
	width: 100%;
	height: 100%;
	padding: 16px;
	text-align: center;
	color: var(--fgTransparentWeak);
}

.storyText {
	position: absolute;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	max-width: 92%;
	overflow-wrap: anywhere;
	text-align: center;
	font-weight: 700;
	line-height: 1.2;
	white-space: pre-wrap;
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
	border-radius: 4px;
	pointer-events: none;
}

.viewerList {
	display: grid;
	gap: 8px;
	padding: 10px;
	border-radius: 8px;
	background: var(--bg);
	border: solid 1px var(--divider);
}

.viewerRow {
	display: grid;
	grid-template-columns: 32px minmax(0, 1fr) auto;
	align-items: center;
	gap: 8px;
}

.avatar {
	width: 32px;
	height: 32px;
}

.viewerName {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.viewedAt {
	color: var(--fgTransparentWeak);
	font-size: 0.85em;
}

@media (max-width: 500px) {
	.viewerRow {
		grid-template-columns: 32px minmax(0, 1fr);
	}

	.viewedAt {
		grid-column: 2;
	}
}
</style>
