<template>
	<section class="project-storage">
		<div v-if="!files.length" class="project-view-empty">
			<ProjectStorageEmptyGraphic />
			<h2>
				Browse your files
			</h2>
			<p>
				Upload and share important files and attachments with project members
			</p>
			<w-button
				bg-color="primary"
				color="white"
				:loading="isUploading"
				@click="requestUpload"
			>
				Upload file
				<w-icon class="right">
					mdi mdi-upload
				</w-icon>
			</w-button>
		</div>

		<Container v-else class="project-storage__content">
			<section>
				<VindiTitle icon="mdi mdi-clipboard-check-outline">
					File storage
				</VindiTitle>
				<div class="project-storage__list">
					<div class="project-storage__header">
						<div>
							<div class="simple-subtitle">
								Sorting
							</div>
							<w-select
								v-model="sortMethod"
								:items="sortMethods"
								bg-color="transparent"
							/>
						</div>
						<w-divider vertical />
						<VindiSpacer />
						<w-input
							v-model="fileSearch"
							class="project-storage__search"
							inner-icon-left="mdi mdi-magnify"
							placeholder="Search task..."
							bg-color="shade-3"
							autocomplete="off"
							color="#eee"
							name="search"
						/>
						<w-button
							bg-color="primary"
							color="white"
							:loading="isUploading"
							@click="requestUpload"
						>
							Upload file
							<w-icon class="right">
								mdi mdi-upload
							</w-icon>
						</w-button>
					</div>
					<w-divider class="nm" />
					<w-table
						:headers="headers"
						:items="fileList"
						no-headers
					>
						<template #[`item-cell.name`]="{ item }">
							<w-flex align-center>
								<div
									class="project-storage__preview"
									:style="computePreviewStyle(item)"
								>
									<w-icon xl>
										mdi mdi-file-document
									</w-icon>
								</div>
								<div>
									<div
										class="project-storage__name"
										:title="`Open ${item.name}`"
										@click="openAttachment(item)"
									>
										{{ item.name }}
									</div>
									<div class="project-storage__size">
										Uploaded {{ formatDate(item) }} by {{ item.uploadedBy.fullName }} • {{ item.size }}
									</div>
								</div>
							</w-flex>
						</template>

						<template #[`item-cell.task`]="{ item }">
							<template v-if="item.task">
								Attached to 
								<span
									class="project-storage__task-id"
									@click="openTask(item.task)"
								>
									#{{ item.task.localId }}
								</span>
							</template>
							<div v-else class="project-storage__task-missing">
								No task attached
							</div>
						</template>

						<template #[`item-cell.options`]="{ item }">
							<w-button
								icon="mdi mdi-download"
								color="primary"
								text
								xl
								@click="requestDownload(item)"
							/>
							<w-button
								icon="mdi mdi-delete"
								color="error"
								text
								lg
								@click="requestDelete(item)"
							/>
						</template>

						<template #no-data>
							No files match your search criteria
						</template>
					</w-table>
					<w-divider class="nm" />
					<VindiPagination
						v-model="currentPage"
						:total="pageCount"
						:visible="5"
						class="project-storage__pagination"
					/>
				</div>
			</section>
		</Container>

		<!-- ANCHOR Deletion dialog -->
		<w-dialog
			v-model="deletionDialog"
			width="350"
		>
			<VindiTitle icon="mdi mdi-delete">
				Delete File
			</VindiTitle>

			<w-divider />

			<div style="padding-bottom: 10px">
				<p style="padding-bottom: 10px">
					Are you sure?
				</p>
				<small>
					You are about to permanently delete this file. This will also
					remove the file as attachment from any task it is connected to.
				</small>
			</div>
			<w-flex>
				<w-button
					text
					:disabled="deletionActive"
					@click="deletionDialog = false"
				>
					<w-icon class="left">
						mdi mdi-arrow-left
					</w-icon>
					Back
				</w-button>
				<VindiSpacer />
				<w-button
					bg-color="error"
					text
					:loading="deletionActive"
					@click="confirmDelete"
				>
					Confirm
				</w-button>
			</w-flex>
		</w-dialog>
	</section>
</template>

<script lang="ts">
import Vue from 'vue';
import { downloadURL, formatBytes, IMAGE_FILES, openFilePicker, parseSlug } from '~/src/helpers';
import fetchStoragePage from '~/assets/query/storage/fetchStoragePage.gql';
import deleteFile from '~/assets/query/storage/deleteFile.gql';
import dayjs from 'dayjs';
import gql from 'graphql-tag';

export default Vue.extend({

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

		return {
			project: result.project,
			files: result.project.attachments
		};
	},

	data: () => ({
		project: null as any,
		files: [] as any[],
		selected: null as any,
		isUploading: false,
		manager: null as any,
		currentPage: 0,
		filesPerPage: 10,
		fileSearch: '',
		deletionDialog: false,
		deletionActive: false,

		// Sorting
		sortMethod: 'ALPHABETICAL',
		sortMethods: [
			{ label: 'Alphabetical', value: 'ALPHABETICAL' },
			{ label: 'Upload date', value: 'UPLOAD_DATE' },
			{ label: 'File size', value: 'FILE_SIZE' }
		],
		sortFunctions: {
			ALPHABETICAL: 'sortAlphabetical',
			UPLOAD_DATE: 'sortUploadDate',
			FILE_SIZE: 'sortFileSize',
		} as any,

		// Headers
		headers: [
			{ key: 'name' },
			{ key: 'task', align: 'right' },
			{ key: 'options' }
		],
	}),

	computed: {

		fileList(): any {
			const search = this.fileSearch.toLowerCase();
			const method = this.sortFunctions[this.sortMethod];
			const func = (this as any)[method];

			if(!func) {
				throw new Error('Unexpected sort method');
			}

			return [
				...this.files
			].filter((file) => {
				return file.name.toLowerCase().includes(search);
			}).map((file) => ({
				...file,
				size: formatBytes(file.fileSize)
			})).sort(func);
		},

		pageCount(): number {
			return Math.ceil(this.fileList.length / this.filesPerPage);
		}
	},

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

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

		// Subscription builder utility
		function createQuery(field: string): any {
			return gql`
				subscription ($pid: Int!) {
					${field}(projectId: $pid) {
						id
					}
				}
			`;
		}
		
		this.manager.subscribe({
			query: createQuery('attachmentUploaded'),
			next: this.refetchFiles,
			variables: {
				pid: slug
			}
		});

		this.manager.subscribe({
			query: createQuery('attachmentUpdated'),
			next: this.refetchFiles,
			variables: {
				pid: slug
			}
		});

		this.manager.subscribe({
			query: createQuery('attachmentRemoved'),
			next: this.refetchFiles,
			variables: {
				pid: slug
			}
		});
	},

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

	methods: {
		async refetchFiles() {
			const res = await this.$api.query(fetchStoragePage, {
				pid: this.project.id
			});

			this.project = res.project;
			this.files = res.project.attachments;
		},

		requestUpload() {
			openFilePicker({ multiple: true }, (...files) => {
				this.uploadFiles(files);
			});
		},

		async uploadFiles(files: File[]) {
			this.isUploading = true;

			const tasks = files.map(file =>
				this.$api.upload('attachment?project=' + this.project.id, file)
			);

			await Promise.all(tasks);

			this.isUploading = false;
		},

		computePreviewStyle(file: any) {
			const isImage = IMAGE_FILES.includes(file.extension);

			return isImage && {
				backgroundImage: `url(${file.filePath})`,
				color: 'transparent'
			};
		},

		openTask(task: any) {
			this.$store.dispatch('openTaskViewer', task.id);
		},

		openAttachment(file: any) {
			window.open(window.location.origin + file.filePath);
		},

		requestDownload(file: any) {
			downloadURL(file.filePath, file.name);
		},

		requestDelete(file: any) {
			this.selected = file;
			this.deletionDialog = true;
		},

		async confirmDelete() {
			this.deletionActive = true;

			await this.$api.query(deleteFile, {
				id: this.selected.id
			});

			this.deletionActive = false;
			this.deletionDialog = false;
		},

		formatDate(file: any): string {
			const lastModified = parseInt(file.uploadedAt);
			const dateFormat = this.$t('SHORT_DATE_FORMAT') as string;

			return dayjs(lastModified).format(dateFormat);
		},

		sortAlphabetical(left: any, right: any): number {
			return left.name.localeCompare(right.name);
		},

		sortFileSize(left: any, right: any): number {
			return right.fileSize - left.fileSize;
		},

		sortUploadDate(left: any, right: any): number {
			const aModified = dayjs(parseInt(left.uploadedAt));
			const bModified = dayjs(parseInt(right.uploadedAt));

			return aModified.isBefore(bModified) ? 1 : -1;
		},
	}

});
</script>

<style lang="scss">
.project-storage {

	&__content {
		@include margin(16, t);
		@include padding(8, b);
	}

	&__list {
		@include shadow;
		@include rounded('3xl');
		@include margin(2, t);
		
		background-color: var(--color-shade-0);
		min-height: 250px;
	}

	&__header {
		@include padding(4);

		display: flex;
		gap: 1.25rem;

		.simple-subtitle {
			padding-bottom: 0;
		}

		.w-select__selection-wrap, input {
			padding-right: 0;
			padding-left: 0;
		}
	}

	&__filter {
		@include margin(3.5, t);
		@include font-weight(medium);

		cursor: pointer;
	}

	&__search {
		max-width: 20rem;
	}

	.w-table__cell {
		height: 4.5rem;
		

		&:not(:first-child) {
			white-space: nowrap;
			width: 0;
		}

		&:last-child {
			@include padding(8, l);
		}
	}

	&__preview {
		@include rounded(lg);
		@include margin(4, r);

		color: gray;
		display: flex;
		align-items: center;
		justify-content: center;
		width: 2.75rem;
		height: 2.75rem;
		background-color: var(--color-shade-2);
		background-size: cover;
		background-position: center;
	}

	&__name {
		@include font-weight(medium);

		cursor: pointer;

		&:hover {
			text-decoration: underline;
		}
	}

	&__size {
		@include font-size(sm);

		color: gray;
	}

	&__task-id {
		@include font-weight(bold);
		@include font-size(lg);

		color: var(--accent-color);
		cursor: pointer;
	}

	&__task-missing {
		color: gray;
	}

	&__pagination {
		@include padding(4);
	}
}
</style>