<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<div
	:class="[$style.root, { [$style.draghover]: draghover }]"
	draggable="true"
	:title="title"
	@click="onClick"
	@contextmenu.stop="onContextmenu"
	@mouseover="onMouseover"
	@mouseout="onMouseout"
	@dragover.prevent.stop="onDragover"
	@dragenter.prevent="onDragenter"
	@dragleave="onDragleave"
	@drop.prevent.stop="onDrop"
	@dragstart="onDragstart"
	@dragend="onDragend"
>
	<p :class="$style.name">
		<template v-if="hover"><i :class="$style.icon" class="ti ti-folder ti-fw"></i></template>
		<template v-if="!hover"><i :class="$style.icon" class="ti ti-folder ti-fw"></i></template>
		{{ folder.name }}
	</p>
	<p v-if="defaultStore.state.uploadFolder == folder.id" :class="$style.upload">
		{{ i18n.ts.uploadFolder }}
	</p>
	<button v-if="selectMode" class="_button" :class="[$style.checkbox, { [$style.checked]: isSelected }]" @click.prevent.stop="checkboxClicked"></button>
</div>
</template>

<script lang="ts" setup>
import { computed, defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from '@/store.js';
import { claimAchievement } from '@/scripts/achievements.js';
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
import { MenuItem } from '@/types/menu.js';

const props = withDefaults(defineProps<{
	folder: Misskey.entities.DriveFolder;
	isSelected?: boolean;
	selectMode?: boolean;
  selectedFiles?: string[];
}>(), {
	isSelected: false,
	selectMode: false,
});

const emit = defineEmits<{
	(ev: 'chosen', v: Misskey.entities.DriveFolder): void;
	(ev: 'move', v: Misskey.entities.DriveFolder): void;
	(ev: 'upload', file: File, folder: Misskey.entities.DriveFolder);
	(ev: 'removeFile', v: Misskey.entities.DriveFile['id']): void;
	(ev: 'removeFolder', v: Misskey.entities.DriveFolder['id']): void;
	(ev: 'dragstart'): void;
	(ev: 'dragend'): void;
}>();

const hover = ref(false);
const draghover = ref(false);
const isDragging = ref(false);

const title = computed(() => props.folder.name);

function checkboxClicked() {
	emit('chosen', props.folder);
}

function onClick() {
	emit('move', props.folder);
}

function onMouseover() {
	hover.value = true;
}

function onMouseout() {
	hover.value = false;
}

function onDragover(ev: DragEvent) {
	if (!ev.dataTransfer) return;

	// 自分自身がドラッグされている場合
	if (isDragging.value) {
		// 自分自身にはドロップさせない
		ev.dataTransfer.dropEffect = 'none';
		return;
	}

	const isFile = ev.dataTransfer.items[0].kind === 'file';
	const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
	const isDriveFolder = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FOLDER_;

	if (isFile || isDriveFile || isDriveFolder) {
		switch (ev.dataTransfer.effectAllowed) {
			case 'all':
			case 'uninitialized':
			case 'copy':
			case 'copyLink':
			case 'copyMove':
				ev.dataTransfer.dropEffect = 'copy';
				break;
			case 'linkMove':
			case 'move':
				ev.dataTransfer.dropEffect = 'move';
				break;
			default:
				ev.dataTransfer.dropEffect = 'none';
				break;
		}
	} else {
		ev.dataTransfer.dropEffect = 'none';
	}
}

function onDragenter() {
	if (!isDragging.value) draghover.value = true;
}

function onDragleave() {
	draghover.value = false;
}

function onDrop(ev: DragEvent) {
	draghover.value = false;

	if (!ev.dataTransfer) return;

	// ファイルだったら
	if (ev.dataTransfer.files.length > 0) {
		for (const file of Array.from(ev.dataTransfer.files)) {
			emit('upload', file, props.folder);
		}
		return;
	}

	//#region ドライブのファイル
	const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
	if (driveFile != null && driveFile !== '') {
		const file = JSON.parse(driveFile);
		emit('removeFile', file.id);
		if (props.selectedFiles.length > 0) {
			props.selectedFiles.forEach((e) => {
				misskeyApi('drive/files/update', {
					fileId: e.id,
					folderId: props.folder.id,
				});
			});
		} else {
			misskeyApi('drive/files/update', {
				fileId: file.id,
				folderId: props.folder.id,
			});
		}
	}
	//#endregion

	//#region ドライブのフォルダ
	const driveFolder = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FOLDER_);
	if (driveFolder != null && driveFolder !== '') {
		const folder = JSON.parse(driveFolder);

		// 移動先が自分自身ならreject
		if (folder.id === props.folder.id) return;

		emit('removeFolder', folder.id);
		misskeyApi('drive/folders/update', {
			folderId: folder.id,
			parentId: props.folder.id,
		}).then(() => {
			// noop
		}).catch(err => {
			switch (err.code) {
				case 'RECURSIVE_NESTING':
					claimAchievement('driveFolderCircularReference');
					os.alert({
						type: 'error',
						title: i18n.ts.unableToProcess,
						text: i18n.ts.circularReferenceFolder,
					});
					break;
				default:
					os.alert({
						type: 'error',
						text: i18n.ts.somethingHappened,
					});
			}
		});
	}
	//#endregion
}

function onDragstart(ev: DragEvent) {
	if (!ev.dataTransfer) return;

	ev.dataTransfer.effectAllowed = 'move';
	ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FOLDER_, JSON.stringify(props.folder));
	isDragging.value = true;

	// 親ブラウザに対して、ドラッグが開始されたフラグを立てる
	// (=あなたの子供が、ドラッグを開始しましたよ)
	emit('dragstart');
}

function onDragend() {
	isDragging.value = false;
	emit('dragend');
}

function go() {
	emit('move', props.folder);
}

function rename() {
	os.inputText({
		title: i18n.ts.renameFolder,
		placeholder: i18n.ts.inputNewFolderName,
		default: props.folder.name,
	}).then(({ canceled, result: name }) => {
		if (canceled) return;
		misskeyApi('drive/folders/update', {
			folderId: props.folder.id,
			name: name,
		});
	});
}

function deleteFolder() {
	misskeyApi('drive/folders/show', {
		folderId: props.folder.id,
	}).then(async (r) => {
		if (r.foldersCount > 0) {
			await os.alert({
				type: 'error',
				title: i18n.ts.unableToDelete,
				text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。',
			});
		}

		if (r.filesCount > 0) {
			const { canceled } = await os.confirm({
				type: 'warning',
				text: i18n.t('driveFolderDeleteConfirm', { name: props.folder.name }),
			});

			if (canceled) return;

			let allResults = [];
			let Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31 });
			allResults = allResults.concat(Result);
			while (Result.length >= 31) {
				const untilId = Result[Result.length - 1].id;
				Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31, untilId });
				allResults = allResults.concat(Result); // pushをconcatに変更
			}
			allResults.forEach((r, i) => {
				misskeyApi('drive/files/delete', { fileId: r.id });
			});

			misskeyApi('drive/folders/show', {
				folderId: props.folder.id,
			}).then(async (r) => {
				if (r.filesCount > 0) {
					let allResults = [];
					let Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31 });
					allResults = allResults.concat(Result);
					while (Result.length >= 31) {
						const untilId = Result[Result.length - 1].id;
						Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31, untilId });
						allResults = allResults.concat(Result);
					}
					allResults.forEach((r, i) => {
						misskeyApi('drive/files/delete', { fileId: r.id });
					});

					misskeyApi('drive/folders/delete', {
						folderId: props.folder.id,
					}).then(() => {
						if (defaultStore.state.uploadFolder === props.folder.id) {
							defaultStore.set('uploadFolder', null);
						}
					}).catch(err => {
						switch (err.id) {
							case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
								os.alert({
									type: 'error',
									title: i18n.ts.unableToDelete,
									text: i18n.ts.hasChildFilesOrFolders,
								});
								break;
							default:
								os.alert({
									type: 'error',
									text: i18n.ts.unableToDelete,
								});
						}
					});

					misskeyApi('drive/folders/delete', {
						folderId: props.folder.id,
					});
				} else {
					misskeyApi('drive/folders/delete', {
						folderId: props.folder.id,
					});
				}
			});
		} else {
			await misskeyApi('drive/folders/delete', {
				folderId: props.folder.id,
			}).then(() => {
				if (defaultStore.state.uploadFolder === props.folder.id) {
					defaultStore.set('uploadFolder', null);
				}
			}).catch(err => {
				switch (err.id) {
					case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
						os.alert({
							type: 'error',
							title: i18n.ts.unableToDelete,
							text: i18n.ts.hasChildFilesOrFolders,
						});
						break;
					default:
						os.alert({
							type: 'error',
							text: i18n.ts.unableToDelete,
						});
				}
			});
		}
	});
}

function setAsUploadFolder() {
	defaultStore.set('uploadFolder', props.folder.id);
}

function onContextmenu(ev: MouseEvent) {
	let menu: MenuItem[];
	menu = [{
		text: i18n.ts.openInWindow,
		icon: 'ti ti-app-window',
		action: () => {
			os.popup(defineAsyncComponent(() => import('@/components/MkDriveWindow.vue')), {
				initialFolder: props.folder,
			}, {
			}, 'closed');
		},
	}, { type: 'divider' }, {
		text: i18n.ts.rename,
		icon: 'ti ti-forms',
		action: rename,
	}, { type: 'divider' }, {
		text: i18n.ts.delete,
		icon: 'ti ti-trash',
		danger: true,
		action: deleteFolder,
	}];
	if (defaultStore.state.devMode) {
		menu = menu.concat([{ type: 'divider' }, {
			icon: 'ti ti-id',
			text: i18n.ts.copyFolderId,
			action: () => {
				copyToClipboard(props.folder.id);
			},
		}]);
	}
	os.contextMenu(menu, ev);
}
</script>

<style lang="scss" module>
.root {
	position: relative;
	padding: 8px;
	height: 64px;
	background: var(--driveFolderBg);
	border-radius: 4px;
	cursor: pointer;

	&.draghover {
		&:after {
			content: "";
			pointer-events: none;
			position: absolute;
			top: -4px;
			right: -4px;
			bottom: -4px;
			left: -4px;
			border: 2px dashed var(--focus);
			border-radius: 4px;
		}
	}
}

.checkbox {
	position: absolute;
	bottom: 8px;
	right: 8px;
	width: 16px;
	height: 16px;
	background: #fff;
	border: solid 1px #000;

	&.checked {
		background: var(--accent);
	}
}

.name {
	margin: 0;
	font-size: 0.9em;
	color: var(--desktopDriveFolderFg);
}

.icon {
	margin-right: 4px;
	margin-left: 2px;
	text-align: left;
}

.upload {
	margin: 4px 4px;
	font-size: 0.8em;
	text-align: right;
	color: var(--desktopDriveFolderFg);
}
</style>