<template>
	<div class="dkgtipfy" :class="{ wallpaper, isMobile }">
		<XSidebar v-if="!isMobile" />

		<MkStickyContainer class="contents">
			<template #header
				><XStatusBars :class="$style.statusbars"
			/></template>
			<main
				id="maincontent"
				style="min-width: 0"
				:style="{ background: pageMetadata?.value?.bg }"
				@contextmenu.stop="onContextmenu"
			>
				<RouterView />
			</main>
		</MkStickyContainer>

		<div v-if="isDesktop" ref="widgetsEl" class="widgets-container">
			<XWidgets @mounted="attachSticky" />
		</div>

		<button
			v-if="!isDesktop && !isMobile"
			class="widgetButton _button"
			@click="widgetsShowing = true"
		>
			<i class="ph-stack ph-bold ph-lg"></i>
		</button>

		<div v-if="isMobile" class="buttons">
			<button
				:aria-label="i18n.t('menu')"
				class="button nav _button"
				@click="drawerMenuShowing = true"
			>
				<div class="button-wrapper">
					<i class="ph-list ph-bold ph-lg"></i
					><span
						v-if="menuIndicated"
						class="indicator"
						:class="{ animateIndicator: $store.state.animation }"
						><i class="ph-circle ph-fill"></i
					></span>
				</div>
			</button>
			<button
				:aria-label="i18n.t('home')"
				class="button home _button"
				@click="
					mainRouter.currentRoute.value.name === 'index'
						? top()
						: mainRouter.push('/');
					updateButtonState();
				"
			>
				<div
					class="button-wrapper"
					:class="buttonAnimIndex === 0 ? 'on' : ''"
				>
					<i class="ph-house ph-bold ph-lg"></i>
				</div>
			</button>
			<button
				:aria-label="i18n.t('notifications')"
				class="button notifications _button"
				@click="
					mainRouter.push('/my/notifications');
					updateButtonState();
				"
			>
				<div
					class="button-wrapper"
					:class="buttonAnimIndex === 1 ? 'on' : ''"
				>
					<i class="ph-bell ph-bold ph-lg"></i
					><span
						v-if="$i?.hasUnreadNotification"
						class="indicator"
						:class="{ animateIndicator: $store.state.animation }"
						><i class="ph-circle ph-fill"></i
					></span>
				</div>
			</button>
			<button
				:aria-label="i18n.t('messaging')"
				class="button messaging _button"
				@click="
					mainRouter.push('/my/messaging');
					updateButtonState();
				"
			>
				<div
					class="button-wrapper"
					:class="buttonAnimIndex === 2 ? 'on' : ''"
				>
					<i class="ph-chats-teardrop ph-bold ph-lg"></i
					><span
						v-if="$i?.hasUnreadMessagingMessage"
						class="indicator"
						:class="{ animateIndicator: $store.state.animation }"
						><i class="ph-circle ph-fill"></i
					></span>
				</div>
			</button>
			<button
				:aria-label="i18n.t('_deck._columns.widgets')"
				class="button widget _button"
				@click="widgetsShowing = true"
			>
				<div class="button-wrapper">
					<i class="ph-stack ph-bold ph-lg"></i>
				</div>
			</button>
		</div>

		<button
			v-if="isMobile && mainRouter.currentRoute.value.name === 'index'"
			ref="postButton"
			:aria-label="i18n.t('note')"
			class="postButton button post _button"
			@click="os.post()"
		>
			<i class="ph-pencil ph-bold ph-lg"></i>
		</button>
		<button
			v-if="
				isMobile && mainRouter.currentRoute.value.name === 'messaging'
			"
			ref="postButton"
			class="postButton button post _button"
			:aria-label="i18n.t('startMessaging')"
			@click="messagingStart"
		>
			<i class="ph-user-plus ph-bold ph-lg"></i>
		</button>

		<transition :name="$store.state.animation ? 'menuDrawer-back' : ''">
			<div
				v-if="drawerMenuShowing"
				class="menuDrawer-back _modalBg"
				@click="drawerMenuShowing = false"
				@touchstart.passive="drawerMenuShowing = false"
			></div>
		</transition>

		<transition :name="$store.state.animation ? 'menuDrawer' : ''">
			<XDrawerMenu v-if="drawerMenuShowing" class="menuDrawer" />
		</transition>

		<transition :name="$store.state.animation ? 'widgetsDrawer-back' : ''">
			<div
				v-if="widgetsShowing"
				class="widgetsDrawer-back _modalBg"
				@click="widgetsShowing = false"
				@touchstart.passive="widgetsShowing = false"
			></div>
		</transition>

		<transition :name="$store.state.animation ? 'widgetsDrawer' : ''">
			<XWidgets v-if="widgetsShowing" class="widgetsDrawer" />
		</transition>

		<XCommon />
	</div>
</template>

<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, provide, ref } from "vue";
import * as Acct from "iceshrimp-js/built/acct";
import type { ComputedRef } from "vue";
import XCommon from "./_common_/common.vue";
import type { PageMetadata } from "@/scripts/page-metadata";
import { instanceName, ui } from "@/config";
import XDrawerMenu from "@/ui/_common_/navbar-for-mobile.vue";
import XSidebar from "@/ui/_common_/navbar.vue";
import * as os from "@/os";
import { defaultStore } from "@/store";
import { navbarItemDef } from "@/navbar";
import { i18n } from "@/i18n";
import { $i } from "@/account";
import { mainRouter } from "@/router";
import {
	provideMetadataReceiver,
	setPageMetadata,
} from "@/scripts/page-metadata";
import { deviceKind } from "@/scripts/device-kind";

const XWidgets = defineAsyncComponent(() => import("./universal.widgets.vue"));
const XStatusBars = defineAsyncComponent(
	() => import("@/ui/_common_/statusbars.vue"),
);

const DESKTOP_THRESHOLD = 1100;
const MOBILE_THRESHOLD = 500;

// デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
const isMobile = ref(
	deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD,
);
window.addEventListener("resize", () => {
	isMobile.value =
		deviceKind === "smartphone" || window.innerWidth <= MOBILE_THRESHOLD;
});

const buttonAnimIndex = ref(0);
const drawerMenuShowing = ref(false);

let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
const widgetsEl = $ref<HTMLElement>();
const postButton = $ref<HTMLElement>();
const widgetsShowing = $ref(false);

provide("router", mainRouter);
provideMetadataReceiver((info) => {
	pageMetadata = info;
	if (pageMetadata.value) {
		document.title = `${pageMetadata.value.title} | ${instanceName}`;
	}
});

const menuIndicated = computed(() => {
	for (const def in navbarItemDef) {
		if (def === "notifications") continue; // 通知は下にボタンとして表示されてるから
		if (navbarItemDef[def].indicated) return true;
	}
	return false;
});

function updateButtonState(): void {
	const routerState = window.location.pathname;
	if (routerState === "/") {
		buttonAnimIndex.value = 0;
		return;
	}
	if (routerState.includes("/my/notifications")) {
		buttonAnimIndex.value = 1;
		return;
	}
	if (routerState.includes("/my/messaging")) {
		buttonAnimIndex.value = 2;
		return;
	}
	buttonAnimIndex.value = 3;
}

updateButtonState();

mainRouter.on("change", () => {
	drawerMenuShowing.value = false;
	updateButtonState();
});

if (defaultStore.state.widgets.length === 0) {
	defaultStore.set("widgets", [
		{
			name: "notifications",
			id: "a",
			place: "right",
			data: {},
		},
		{
			name: "trends",
			id: "b",
			place: "right",
			data: {},
		},
	]);
}

function messagingStart(ev) {
	os.popupMenu(
		[
			{
				text: i18n.ts.messagingWithUser,
				icon: "ph-user ph-bold ph-lg",
				action: () => {
					startUser();
				},
			},
			{
				text: i18n.ts.messagingWithGroup,
				icon: "ph-users-three ph-bold ph-lg",
				action: () => {
					startGroup();
				},
			},
			{
				text: i18n.ts.manageGroups,
				icon: "ph-user-circle-gear ph-bold ph-lg",
				action: () => {
					mainRouter.push("/my/groups");
				},
			},
		],
		ev.currentTarget ?? ev.target,
	);
}

async function startUser(): void {
	os.selectUser().then((user) => {
		mainRouter.push(`/my/messaging/${Acct.toString(user)}`);
	});
}

async function startGroup(): void {
	const groups1 = await os.api("users/groups/owned");
	const groups2 = await os.api("users/groups/joined");
	if (groups1.length === 0 && groups2.length === 0) {
		os.alert({
			type: "warning",
			title: i18n.ts.youHaveNoGroups,
			text: i18n.ts.joinOrCreateGroup,
		});
		return;
	}
	const { canceled, result: group } = await os.select({
		title: i18n.ts.group,
		items: groups1.concat(groups2).map((group) => ({
			value: group,
			text: group.name,
		})),
	});
	if (canceled) return;
	mainRouter.push(`/my/messaging/group/${group.id}`);
}

onMounted(() => {
	if (!isDesktop.value) {
		matchMedia(`(min-width: ${DESKTOP_THRESHOLD - 1}px)`).onchange = (
			mql,
		) => {
			if (mql.matches) isDesktop.value = true;
		};
	}
});

const onContextmenu = (ev: MouseEvent) => {
	const isLink = (el: HTMLElement) => {
		if (el.tagName === "A") return true;
		if (el.parentElement) {
			return isLink(el.parentElement);
		}
	};
	if (isLink(ev.target)) return;
	if (
		["INPUT", "TEXTAREA", "IMG", "VIDEO", "CANVAS"].includes(
			ev.target.tagName,
		) ||
		ev.target.attributes.contenteditable
	)
		return;
	if (window.getSelection()?.toString() !== "") return;
	const path = mainRouter.getCurrentPath();
	os.contextMenu(
		[
			{
				type: "label",
				text: path,
			},
			{
				icon: "ph-browser ph-bold ph-lg",
				text: i18n.ts.openInWindow,
				action: () => {
					os.pageWindow(path);
				},
			},
		],
		ev,
	);
};

const attachSticky = (el: any) => {
	let lastScrollTop = 0;
	addEventListener(
		"scroll",
		() => {
			requestAnimationFrame(() => {
				widgetsEl.scrollTop += window.scrollY - lastScrollTop;
				lastScrollTop = window.scrollY;
			});
		},
		{ passive: true },
	);
	widgetsEl.classList.add("hide-scrollbar");
	widgetsEl.onmouseenter = () => {
		if (document.documentElement.scrollHeight <= window.innerHeight) {
			widgetsEl.classList.remove("hide-scrollbar");
		} else {
			widgetsEl.classList.add("hide-scrollbar");
		}
	};
};

function top() {
	window.scroll({ top: 0, behavior: "smooth" });
}

const wallpaper = localStorage.getItem("wallpaper") != null;
console.log(mainRouter.currentRoute.value.name);
</script>

<style lang="scss" scoped>
.widgetsDrawer-enter-active,
.widgetsDrawer-leave-active {
	opacity: 1;
	transform: translateX(0);
	transition:
		transform 300ms cubic-bezier(0.23, 1, 0.32, 1),
		opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.widgetsDrawer-enter-from,
.widgetsDrawer-leave-active {
	opacity: 0;
	transform: translateX(240px);
}

.widgetsDrawer-back-enter-active,
.widgetsDrawer-back-leave-active {
	opacity: 1;
	transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.widgetsDrawer-back-enter-from,
.widgetsDrawer-back-leave-active {
	opacity: 0;
}

.menuDrawer-enter-active,
.menuDrawer-leave-active {
	opacity: 1;
	transform: translateX(0);
	transition:
		transform 300ms cubic-bezier(0.23, 1, 0.32, 1),
		opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.menuDrawer-enter-from,
.menuDrawer-leave-active {
	opacity: 0;
	transform: translateX(-240px);
}

.menuDrawer-back-enter-active,
.menuDrawer-back-leave-active {
	opacity: 1;
	transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.menuDrawer-back-enter-from,
.menuDrawer-back-leave-active {
	opacity: 0;
}

.dkgtipfy {
	$ui-font-size: 1em; // TODO: どこかに集約したい
	$widgets-hide-threshold: 1090px;

	// ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
	min-height: calc(var(--vh, 1vh) * 100);
	box-sizing: border-box;
	display: flex;

	--stickyBottom: 1em;
	&.isMobile {
		--stickyBottom: 6rem;
	}
	&:not(.isMobile) {
		> .contents {
			border-right: 0.5px solid var(--divider);
		}
	}
	&.wallpaper {
		background: var(--wallpaperOverlay);

		:deep(.sidebar .middle) {
			position: relative;
			&::before {
				content: "";
				position: absolute;
				inset: -10px 10px;
				background: var(--bg);
				border-radius: calc((2.85rem / 2) + 5px);
				opacity: 1;
				z-index: -3;
			}
			> ._button:last-child {
				margin-bottom: 0 !important;
			}
		}
	}

	> .contents {
		width: 100%;
		min-width: 0;
		$widgets-hide-threshold: 1090px;
		overflow-x: clip;
		@media (max-width: $widgets-hide-threshold) {
			padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 96px);
		}
	}

	> .widgets-container {
		position: sticky;
		top: 0;
		max-height: 100vh;
		overflow-y: auto;
		padding: 0 var(--margin);
		width: 300px;
		min-width: max-content;
		box-sizing: content-box;

		@media (max-width: $widgets-hide-threshold) {
			display: none;
		}
	}

	> .widgetsDrawer-back {
		z-index: 1001;
	}

	> .widgetsDrawer {
		position: fixed;
		top: 0;
		right: 0;
		z-index: 1001;
		// ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
		height: calc(var(--vh, 1vh) * 100);
		padding: var(--margin);
		box-sizing: border-box;
		overflow: auto;
		overscroll-behavior: contain;
		background: var(--bg);
	}

	> .postButton,
	.widgetButton {
		bottom: var(--stickyBottom);
		right: 1.5rem;
		height: 4rem;
		width: 4rem;
		background-position: center;
		background: linear-gradient(
			90deg,
			var(--buttonGradateA),
			var(--buttonGradateB)
		);
		color: var(--fgOnAccent);
		position: fixed !important;
		z-index: 1000;
		font-size: 16px;
		border-radius: 10px;
		box-shadow: var(--shadow) 0px 0px 25px;
		transition: background 0.6s;
		transition: transform 0.3s;

		> .isHidden {
			transform: scale(0);
		}

		> .isVisible {
			transform: scale(1);
		}

		&:active {
			background-color: var(--accentedBg);
			background-size: 100%;
			transition: background 0.1s;
		}
	}

	> .buttons {
		position: fixed;
		z-index: 1000;
		bottom: 0;
		left: 0;
		padding: 12px 12px calc(env(safe-area-inset-bottom, 0px) + 12px) 12px;
		display: flex;
		width: 100%;
		box-sizing: border-box;
		background-color: var(--bg);

		> .button {
			position: relative;
			flex: 1;
			padding: 0;
			margin: auto;
			height: 3.5rem;
			border-radius: 8px;
			background-position: center;
			transition: background 0.6s;
			color: var(--fg);

			> .button-wrapper {
				display: inline-flex;
				justify-content: center;
				padding: 4px 0;

				&.on {
					background-color: var(--focus);
					width: 100%;
					border-radius: 999px;
					transition: all 0.4s ease-in-out;
				}

				> .indicator {
					position: absolute;
					top: 0;
					left: 0;
					color: var(--indicator);
					font-size: 16px;
				}

				> .animateIndicator {
				}
			}

			&:not(:last-child) {
				margin-right: 12px;
			}

			@media (max-width: 400px) {
				height: 60px;

				&:not(:last-child) {
					margin-right: 8px;
				}
			}
			> .indicator {
				position: absolute;
				top: 0;
				left: 0;
				color: var(--indicator);
				font-size: 16px;
			}

			> .animateIndicator {
			}

			&:first-child {
				margin-left: 0;
			}

			&:last-child {
				margin-right: 0;
			}

			> * {
				font-size: 16px;
			}

			&:disabled {
				cursor: default;

				> * {
					opacity: 0.5;
				}
			}
		}
	}

	> .menuDrawer-back {
		z-index: 1001;
	}

	> .menuDrawer {
		position: fixed;
		top: 0;
		left: 0;
		z-index: 1001;
		// ほんとは単に 100vh と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
		height: calc(var(--vh, 1vh) * 100);
		width: 240px;
		box-sizing: border-box;
		contain: strict;
		overflow: auto;
		overscroll-behavior: contain;
		background: var(--navBg);
	}
}
</style>

<style lang="scss" module>
.statusbars {
	position: sticky;
	top: 0;
	left: 0;
}
</style>
