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

<template>
<div class="_gaps">
	<div :class="$style.header">
		<MkSelect v-model="type" :class="$style.typeSelect">
			<option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option>
			<option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option>
			<option value="isSuspended">{{ i18n.ts._role._condition.isSuspended }}</option>
			<option value="isLocked">{{ i18n.ts._role._condition.isLocked }}</option>
			<option value="isBot">{{ i18n.ts._role._condition.isBot }}</option>
			<option value="isCat">{{ i18n.ts._role._condition.isCat }}</option>
			<option value="isExplorable">{{ i18n.ts._role._condition.isExplorable }}</option>
			<option value="roleAssignedTo">{{ i18n.ts._role._condition.roleAssignedTo }}</option>
			<option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option>
			<option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option>
			<option value="followersLessThanOrEq">{{ i18n.ts._role._condition.followersLessThanOrEq }}</option>
			<option value="followersMoreThanOrEq">{{ i18n.ts._role._condition.followersMoreThanOrEq }}</option>
			<option value="followingLessThanOrEq">{{ i18n.ts._role._condition.followingLessThanOrEq }}</option>
			<option value="followingMoreThanOrEq">{{ i18n.ts._role._condition.followingMoreThanOrEq }}</option>
			<option value="notesLessThanOrEq">{{ i18n.ts._role._condition.notesLessThanOrEq }}</option>
			<option value="notesMoreThanOrEq">{{ i18n.ts._role._condition.notesMoreThanOrEq }}</option>
			<option value="and">{{ i18n.ts._role._condition.and }}</option>
			<option value="or">{{ i18n.ts._role._condition.or }}</option>
			<option value="not">{{ i18n.ts._role._condition.not }}</option>
		</MkSelect>
		<button v-if="draggable" class="drag-handle _button" :class="$style.dragHandle">
			<i class="ti ti-menu-2"></i>
		</button>
		<button v-if="draggable" class="_button" :class="$style.remove" @click="removeSelf">
			<i class="ti ti-x"></i>
		</button>
	</div>

	<div v-if="type === 'and' || type === 'or'" class="_gaps">
		<div ref="dndParentEl" class="_gaps">
			<div v-for="valueId in valueIds" :key="valueId" :class="$style.item">
				<RolesEditorFormula :modelValue="valueKv[valueId]" draggable @update:modelValue="valuesItemUpdated" @remove="removeItem(valueId)"/>
			</div>
		</div>
		<MkButton rounded style="margin: 0 auto;" @click="addValue"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
	</div>

	<div v-else-if="assertValueNot(v)" :class="$style.item">
		<RolesEditorFormula v-model="v.value"/>
	</div>

	<MkInput v-else-if="['createdMoreThan', 'createdLessThan'].includes(type) && 'sec' in v" v-model="v.sec" type="number">
		<template #suffix>sec</template>
	</MkInput>

	<MkInput v-else-if="['followersLessThanOrEq', 'followersMoreThanOrEq', 'followingLessThanOrEq', 'followingMoreThanOrEq', 'notesLessThanOrEq', 'notesMoreThanOrEq'].includes(type) && 'value' in v" v-model="v.value" type="number">
	</MkInput>

	<MkSelect v-else-if="type === 'roleAssignedTo' && 'roleId' in v" v-model="v.roleId">
		<option v-for="role in roles.filter(r => r.target === 'manual')" :key="role.id" :value="role.id">{{ role.name }}</option>
	</MkSelect>
</div>
</template>

<script lang="ts" setup>
import { computed, ref, shallowRef, watch } from 'vue';
import { v4 as uuid } from 'uuid';
import * as Misskey from 'misskey-js';
import { animations } from '@formkit/drag-and-drop';
import { dragAndDrop } from '@formkit/drag-and-drop/vue';
import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
import { deepClone } from '@/scripts/clone.js';
import { rolesCache } from '@/cache.js';

const emit = defineEmits<{
	(ev: 'update:modelValue', value: Misskey.entities.RoleCondFormulaValue): void;
	(ev: 'remove'): void;
}>();

const props = defineProps<{
	modelValue: Misskey.entities.RoleCondFormulaValue;
	draggable?: boolean;
}>();

function assertLogicFormula(f: Misskey.entities.RoleCondFormulaValue): f is Misskey.entities.RoleCondFormulaLogics {
	return ['and', 'or'].includes(f.type);
}

function assertValueNot(f: Misskey.entities.RoleCondFormulaValue): f is Misskey.entities.RoleCondFormulaValueNot {
	return f.type === 'not';
}

const dndParentEl = shallowRef<HTMLElement>();
const v = ref(deepClone(props.modelValue));

const valueKv = computed(() => {
	if (assertLogicFormula(v.value)) {
		return Object.fromEntries(v.value.values.map(v => [v.id, v] as const));
	} else {
		return [];
	}
});

function updateValueIds(to: Misskey.entities.RoleCondFormulaValue) {
	if (assertLogicFormula(to)) {
		return to.values.map(v => v.id);
	} else {
		return [];
	}
}

const valueIds = ref(updateValueIds(v.value));

watch(v, () => {
	valueIds.value = updateValueIds(v.value);
}, { deep: true });

dragAndDrop({
	parent: dndParentEl,
	values: valueIds,
	// TODO: v0.2.0時点では親子階層のドラッグアンドドロップは不安定
	//group: 'roleFormula',
	dragHandle: '.drag-handle',
	plugins: [animations()],
	onDragend: () => {
		if (assertLogicFormula(v.value)) {
			v.value.values = valueIds.value.map(id => {
				if (assertLogicFormula(v.value)) {
					return v.value.values.find(v => v.id === id) ?? null;
				} else {
					return null;
				}
			}).filter(v => v !== null);
		}
	},
});

const roles = await rolesCache.fetch();

watch(() => props.modelValue, () => {
	if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return;
	v.value = deepClone(props.modelValue);
}, { deep: true });

watch(v, () => {
	emit('update:modelValue', v.value);
}, { deep: true });

const type = computed({
	get: () => v.value.type,
	set: (t) => {
		v.value.type = t;

		if (v.value.type === 'and') v.value.values = [];
		if (v.value.type === 'or') v.value.values = [];
		if (v.value.type === 'not') v.value.value = { id: uuid(), type: 'isRemote' };
		if (v.value.type === 'roleAssignedTo') v.value.roleId = '';
		if (v.value.type === 'createdLessThan') v.value.sec = 86400;
		if (v.value.type === 'createdMoreThan') v.value.sec = 86400;
		if (v.value.type === 'followersLessThanOrEq') v.value.value = 10;
		if (v.value.type === 'followersMoreThanOrEq') v.value.value = 10;
		if (v.value.type === 'followingLessThanOrEq') v.value.value = 10;
		if (v.value.type === 'followingMoreThanOrEq') v.value.value = 10;
		if (v.value.type === 'notesLessThanOrEq') v.value.value = 10;
		if (v.value.type === 'notesMoreThanOrEq') v.value.value = 10;
	},
});

function addValue() {
	if (!assertLogicFormula(v.value)) return;
	v.value.values.push({ id: uuid(), type: 'isRemote' });
}

function valuesItemUpdated(item: Misskey.entities.RoleCondFormulaValue) {
	if (!assertLogicFormula(v.value)) return;
	const i = v.value.values.findIndex(_item => _item.id === item.id);
	v.value.values[i] = item;
}

function removeItem(itemId: string) {
	if (!assertLogicFormula(v.value)) return;
	v.value.values = v.value.values.filter(_item => _item.id !== itemId);
}

function removeSelf() {
	emit('remove');
}
</script>

<style lang="scss" module>
.header {
	display: flex;
}

.typeSelect {
	flex: 1;
}

.dragHandle {
	cursor: move;
	margin-left: 10px;
}

.remove {
	margin-left: 10px;
}

.item {
	border: solid 2px var(--divider);
	border-radius: var(--radius);
	padding: 12px;

	&:hover {
		border-color: var(--accent);
	}
}
</style>