<template>
	<div class="project-kanban">
		<div v-if="!project.lists.length" class="project-kanban__empty">
			<ProjectKanbanEmptyGraphic
				:color="project.accentColor"
				class="h-44"
			/>
			<h2>
				Get started with kanban!
			</h2>
			<p>
				Introduce a kanban to your project and start categorizing your tasks into lists
			</p>
			<w-button
				bg-color="accent-1"
				color="white"
				@click="requestList"
			>
				Create list
				<w-icon class="right">
					mdi mdi-plus
				</w-icon>
			</w-button>
		</div>
		<template v-else>
			<div
				class="project-kanban__header"
				:style="listHeaderStyle"
			/>
			<Draggable
				v-model="project.lists"
				class="project-kanban__grid"
				handle=".kanban-item__header"
				:animation="200"
				@input="updateListOrder"
			>
				<div
					v-for="list in project.lists" :key="list.id"
					class="kanban-item"
				>
					<div class="kanban-item__header">
						<div class="kanban-item__header-left">
							<w-icon v-if="list.icon" class="left">
								{{ list.icon }}
							</w-icon>
							<h1>
								{{ list.name }}
							</h1>
						</div>
						<div class="kanban-item__header-right">
							<w-button
								icon="mdi mdi-plus"
								color="white"
								lg
								@click="createTask(list)"
							/>
							
							<w-menu
								bottom 
								custom
								align-left
							>
								<template #activator="{ on }">
									<w-button
										icon="mdi mdi-chevron-down"
										color="white"
										lg
										v-on="on"
									/>
								</template>
								<div class="list-menu">
									<p class="list-menu__title">
										{{ $t('LIST_OPTIONS') }}
									</p>
									<w-divider />
									<div class="list-menu__list">
										<div class="list-menu__item">
											<w-icon size="1.1rem">
												mdi mdi-pencil
											</w-icon>
											{{ $t("LIST_EDIT") }}
										</div>
										<div class="list-menu__item">
											<w-icon size="1.1rem">
												mdi mdi-archive
											</w-icon>
											{{ $t("LIST_ARCHIVE") }}
										</div>
										<div class="list-menu__item">
											<w-icon size="1.1rem">
												mdi mdi-arrow-right-bottom-bold
											</w-icon>
											{{ $t("LIST_MOVE") }}
										</div>
										<div class="list-menu__item">
											<w-icon size="1.1rem">
												mdi mdi-content-copy
											</w-icon>
											{{ $t("LIST_COPY") }}
										</div>
										<w-divider />
										<div class="list-menu__item error">
											<w-icon size="1.1rem">
												mdi mdi-delete
											</w-icon>
											{{ $t("LIST_DELETE") }}
										</div>
									</div>
								</div>
							</w-menu>
						</div>
						<div class="kanban-item__divider" />
					</div>
					<Draggable
						v-model="list.tasks"
						class="kanban-item__body"
						group="kanban-task"
						ghost-class="task-tile--ghost"
						:data-id="list.id"
						:force-fallback="true"
						:fallback-tolerance="10"
						@end="updateTaskOrder"
					>
						<TaskTile
							v-for="task in list.tasks"
							:key="task.id"
							:task="task"
							:data-id="task.id"
							class="kanban-item__task"
						/>
					</Draggable>
				</div>
			</Draggable>
		</template>

		<!-- ANCHOR List creation dialog -->
		<w-dialog
			v-model="newListDialog"
			dialog-class="simple-dialog"
			width="600"
		>
			<section-title icon="mdi mdi-view-column">
				{{ $t("CREATE_NEW_LIST") }}
			</section-title>

			<label v-html="$t('CREATE_NEW_LIST_NAME')" />
			<w-input
				v-model="newListName"
			/>

			<label v-html="$t('CREATE_NEW_LIST_ICON')" />
			<icon-picker
				v-model="newListIcon"
			/>

			<div class="simple-dialog__options">
				<spacer />
				<w-button
					color="white"
					bg-color="accent-2"
					:loading="newListLoading"
					:disabled="!newListName"
					@click="createList"
				>
					<w-icon class="left">
						mdi mdi-plus
					</w-icon>
					{{ $t("CREATE_NEW_LIST_CREATE") }}
				</w-button>
			</div>
		</w-dialog>
	</div>
</template>

<script lang="ts">
import Vue from 'vue';
import gql from 'graphql-tag';
import { find, findIndex } from 'lodash';
import { parseSlug, proxyComparator } from '~/src/helpers';
import fetchKanbanPage from '~/assets/query/kanban/fetchKanbanPage.gql';
import onListAdd from '~/assets/query/kanban/onListAdd.gql';
import onListOrder from '~/assets/query/kanban/onListOrder.gql';
import onListRemove from '~/assets/query/kanban/onListRemove.gql';
import onListUpdate from '~/assets/query/kanban/onListUpdate.gql';
import onTaskMove from '~/assets/query/kanban/onTaskMove.gql';
import onTaskRemove from '~/assets/query/kanban/onTaskRemove.gql';
import createList from '~/assets/query/kanban/createList.gql';
import deleteList from '~/assets/query/kanban/deleteList.gql';

export default Vue.extend({

	async asyncData(context) {
		const result = await context.$api.query(fetchKanbanPage, {
			pid: parseSlug(context.params.pid)
		});

		return {
			project: result.project
		};
	},

	data: () => ({
		project: null as any,
		manager: null as any,
		session: Math.floor(Date.now() * Math.random()) % 2147483647,

		newListDialog: false,
		newListLoading: false,
		newListName: '',
		newListIcon: '',
	}),

	computed: {
		listHeaderStyle(): any {
			return {
				backgroundColor: this.project.accentColor
			};
		}
	},

	mounted() {
		this.manager = this.$api.createManager();

		const slug = parseSlug(this.$route.params.pid);

		// TODO Move each "next" handler to a dedicated function
		
		// Task list ordering subscription
		this.manager.subscribe({
			query: onListOrder,
			variables: {
				id: slug
			},
			next: (result: any) => {
				const ordering = result.projectUpdated.lists.map((list: any) => list.id);

				this.project.lists.sort(
					proxyComparator(ordering, 'id')
				);
			}
		});

		// Add new list to kanban
		this.manager.subscribe({
			query: onListAdd,
			variables: {
				id: slug
			},
			next: (result: any) => {
				const list = result.taskListCreated;

				this.project.lists.push({
					...list,
					tasks: []
				});
			}
		});

		// Remove a list from the kanban
		this.manager.subscribe({
			query: onListRemove,
			variables: {
				id: slug
			},
			next: (result: any) => {
				const listId = result.taskListRemoved.id;

				this.project.lists = this.project.lists.filter((list: any) => {
					return list.id != listId;
				});
			}
		});

		// Remove a list from the kanban
		this.manager.subscribe({
			query: onListUpdate,
			variables: {
				id: slug
			},
			next: (result: any) => {
				const listId = result.taskListUpdated.id;
				const index = findIndex(this.project.lists, (list: any) => list.id == listId);
				const current = this.project.lists[index];

				this.project.lists.splice(index, 1, {
					...result.taskListUpdated,
					tasks: current.tasks || []
				});
			}
		});

		// Remove a task from the board
		this.manager.subscribe({
			query: onTaskRemove,
			variables: {
				id: slug
			},
			next: (result: any) => {
				const taskId = result.taskRemoved.id;
				const listId = result.taskRemoved.list?.id;

				if(listId === undefined) {
					return;
				}

				const list = find(this.project.lists, (list: any) => list.id == listId);
			
				if(!list) {
					return;
				}

				const index = findIndex(list.tasks, (task: any) => task.id == taskId);

				if(index >= 0) {
					list.tasks.splice(index, 1);
				}
			}
		});

		// Move a task to a different position
		this.manager.subscribe({
			query: onTaskMove,
			variables: {
				id: slug
			},
			next: (result: any) => {
				if(result.taskMoved.moveId == this.session) {
					return;
				}

				const fromListId = result.taskMoved.fromList?.id;
				const toListId = result.taskMoved.toList?.id;

				// Remove from previous list
				if(fromListId !== undefined) {
					const fromList = find(this.project.lists, (list: any) => list.id == fromListId);
					const oldIndex = findIndex(fromList.tasks, (task: any) => task.id == task.id);

					if(oldIndex >= 0) {
						fromList.tasks.splice(oldIndex, 1);
					}
				}

				// Add to target list
				if(toListId !== undefined) {
					const task = result.taskMoved.task;
					const index = result.taskMoved.index;
					const toList = find(this.project.lists, (list: any) => list.id == toListId);

					toList.tasks.splice(index, 0, task);
				}
			}
		});
	},

	destroyed() {
		this.manager.dispose();
	},

	methods: {

		requestList() {
			this.newListName = '';
			this.newListIcon = '';
			this.newListDialog = true;
		},

		async deleteList(list: any) {
			await this.$api.query(deleteList, {
				id: list.id
			});
		},

		createTask(list: any) {
			this.$store.commit('setCurrentTask', {
				id: undefined,
				summary: '',
				project: this.project,
				list: list
			});
		},

		updateListOrder(list: any) {
			this.$api.query(gql`
				mutation ($id: Int!, $details: ProjectUpdate!) {
					updateProject(id: $id, details: $details) {
						id
					}
				}
			`, {
				id: this.project.id,
				details: {
					listOrder: list.map((list: any) => list.id)
				}
			});
		},

		updateTaskOrder(event: any) {
			const taskId = parseInt(event.item.getAttribute('data-id'));
			const listId = parseInt(event.to.getAttribute('data-id'));

			this.$api.query(gql`
				mutation ($id: Int!, $list: Int!, $index: Int, $session: Int) {
					moveTask(id: $id, listId: $list, index: $index, moveId: $session) {
						id
					}
				}
			`, {
				id: taskId,
				list: listId,
				index: event.newIndex,
				session: this.session
			});
		},

		async createList() {
			this.newListLoading = true;

			await this.$api.query(createList, {
				pid: this.project.id,
				name: this.newListName,
				icon: this.newListIcon
			});

			this.newListLoading = false;
			this.newListDialog = false;

			this.$waveui.notify({
				message: this.$t("NOTIFICATION_CREATE_LIST", [ this.newListName ]),
				success: true,
			});
		}

	}
});
</script>

<style lang="scss">
.project-kanban {
	&__empty {
		@include padding(40, t);

		display: flex;
		align-items: center;
		flex-direction: column;

		h2 {
			@include font-size('2xl');
			@include font-weight(medium);
			@include margin(8, t);

			color: #525252;

			@include dark {
				color: white;
			}
		}

		p {
			@include font-size(lg);
			@include margin(3, t);

			color: #a3a3a3;
			text-align: center;
			max-width: 400px;
		}

		.w-button {
			@include margin(6, t);
		}
	}
}

.project-kanban {
	height: 100%;
	position: relative;

	&__header {
		position: absolute;
		height: 3rem;
		left: 0;
		right: 0;
		top: 0;
	}

	&__grid {
		display: flex;
		height: 100%;
		overflow: hidden;
	}
}

.kanban-item {
	min-width: 340px;
	max-width: 340px;
	display: flex;
	flex-direction: column;

	&__header {
		@include padding(5, x);
		@include padding(2, r);

		height: 3rem;
		cursor: move;
		display: flex;
		align-items: center;
		justify-content: space-between;
		position: relative;
		color: white;

		.w-button {
			margin: 0;
		}
	}

	&__header-left {
		display: flex;
		align-items: center;

		h1 {
			@include font-weight(bold);

			pointer-events: none;
			margin-top: 2px;
		}
	}

	&__header-right {
		display: flex;
		align-items: center;
	}

	&__header-left, &__header-right {
		cursor: auto;
	}

	&__create-button {
		color: white;
		opacity: 0.8;
	}

	&__menu-button {
		margin-left: 0;
		color: white;
		opacity: 0.8;
	}

	&__divider {
		position: absolute;
		right: 0;
		top: 4px;
		height: 40px;
		width: 2px;
		background-color: rgba(255,255,255,0.4);
	}

	&__body {
		@include padding(4);

		flex: 1;
		height: 0;
		overflow-y: auto;
		background-color: #F1F2F6;

		@include dark {
			background-color: #1e2122;
		}

		.sortable-drag {
			opacity: 1;
			transform: rotate(5deg);
		}
	}

	&__task {
		@include margin(4, b);
	}

	&:nth-child(even) &__body {
		background-color: #EAECF2;

		@include dark {
			background-color: #222426;
		}
	}

	::-webkit-scrollbar {
		width: 8px;
	}
}
</style>