<script lang="ts">
import { invalidateAll } from '$app/navigation';
import { m } from '$lib/paraglide/messages.js';
import { getErrorMessage } from '$lib/shared/errors';

const ACCEPTED_TYPES = ['application/pdf', 'image/jpeg', 'image/png', 'image/tiff'];

interface Props {
	onUploadComplete?: (count: number) => void;
}

let { onUploadComplete }: Props = $props();

let isDragging = $state(false);
let windowDragging = $state(false);
let dragCounter = 0;
let isUploading = $state(false);
let uploadProgress = $state(0);
let uploadMessages = $state<{ text: string; isError: boolean; link?: string }[]>([]);
let fileInput: HTMLInputElement;

function handleDragOver(e: DragEvent) {
	e.preventDefault();
	isDragging = true;
}

function handleDragLeave() {
	isDragging = false;
}

async function handleDrop(e: DragEvent) {
	e.preventDefault();
	isDragging = false;
	windowDragging = false;
	dragCounter = 0;
	const files = Array.from(e.dataTransfer?.files ?? []);
	await uploadFiles(files);
}

async function handleFileSelect(e: Event) {
	const input = e.target as HTMLInputElement;
	const files = Array.from(input.files ?? []);
	input.value = '';
	await uploadFiles(files);
}

async function uploadFiles(files: File[]) {
	if (files.length === 0) return;

	const messages: { text: string; isError: boolean; link?: string }[] = [];

	const valid: File[] = [];
	for (const file of files) {
		if (!ACCEPTED_TYPES.includes(file.type)) {
			messages.push({ text: m.upload_invalid_type({ filename: file.name }), isError: true });
		} else {
			valid.push(file);
		}
	}

	if (valid.length === 0) {
		uploadMessages = messages;
		return;
	}

	isUploading = true;
	uploadProgress = 0;
	try {
		const formData = new FormData();
		for (const file of valid) {
			formData.append('files', file);
		}

		const { ok, body } = await new Promise<{ ok: boolean; body: string }>((resolve, reject) => {
			const xhr = new XMLHttpRequest();
			xhr.open('POST', '/api/documents/quick-upload');
			xhr.upload.addEventListener('progress', (e) => {
				if (e.lengthComputable) uploadProgress = Math.round((e.loaded / e.total) * 100);
			});
			xhr.addEventListener('load', () => resolve({ ok: xhr.status < 300, body: xhr.responseText }));
			xhr.addEventListener('error', () => reject(new Error('Network error')));
			xhr.send(formData);
		});

		if (ok) {
			const result = JSON.parse(body);
			if (result.created?.length > 0) {
				messages.push({ text: m.upload_success({ count: result.created.length }), isError: false });
				onUploadComplete?.(result.created.length);
			}
			for (const doc of result.updated ?? []) {
				messages.push({
					text: m.upload_duplicate({ filename: doc.originalFilename }),
					isError: false,
					link: `/documents/${doc.id}`
				});
			}
			for (const err of result.errors ?? []) {
				messages.push({
					text: `${err.filename}: ${getErrorMessage(err.code)}`,
					isError: true
				});
			}
			await invalidateAll();
		} else {
			for (const file of valid) {
				messages.push({ text: m.upload_error({ filename: file.name }), isError: true });
			}
		}
	} finally {
		isUploading = false;
		uploadProgress = 0;
		uploadMessages = messages;
	}
}

$effect(() => {
	function onWindowDragEnter(e: DragEvent) {
		if (!e.dataTransfer?.types.includes('Files')) return;
		dragCounter++;
		windowDragging = true;
	}

	function onWindowDragLeave() {
		dragCounter--;
		if (dragCounter <= 0) {
			dragCounter = 0;
			windowDragging = false;
		}
	}

	function onWindowDrop() {
		dragCounter = 0;
		windowDragging = false;
	}

	window.addEventListener('dragenter', onWindowDragEnter);
	window.addEventListener('dragleave', onWindowDragLeave);
	window.addEventListener('drop', onWindowDrop);

	return () => {
		window.removeEventListener('dragenter', onWindowDragEnter);
		window.removeEventListener('dragleave', onWindowDragLeave);
		window.removeEventListener('drop', onWindowDrop);
	};
});
</script>

<div
	role="button"
	tabindex="0"
	class="mb-4 flex cursor-pointer flex-col items-center justify-center gap-2 border border-dashed px-6 transition-all duration-200 {isDragging
		? 'border-primary bg-accent-bg py-10 text-primary'
		: windowDragging
			? 'border-primary/60 bg-accent-bg/50 py-10 text-primary/80'
			: 'border-ink/30 py-6 text-ink-3 hover:border-primary hover:text-primary'}"
	ondragover={handleDragOver}
	ondragleave={handleDragLeave}
	ondrop={handleDrop}
	onclick={() => fileInput.click()}
	onkeydown={(e) => e.key === 'Enter' && fileInput.click()}
>
	{#if isUploading}
		<div class="flex w-48 flex-col items-center gap-1">
			<div class="h-1.5 w-full overflow-hidden rounded-full bg-ink/10">
				<div
					class="h-full rounded-full bg-primary transition-all duration-200"
					style="width: {uploadProgress}%"
				></div>
			</div>
			<span class="font-sans text-xs text-ink-3">{uploadProgress}%</span>
		</div>
	{:else}
		<img
			src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Copy-Item-MD.svg"
			alt=""
			aria-hidden="true"
			class="h-8 w-8 opacity-40"
		/>
		<div class="flex flex-col items-center gap-0.5 text-center">
			<span class="font-sans text-sm text-ink-2">{m.upload_drop_hint()}</span>
			<span class="font-sans text-xs text-ink-3">{m.upload_accepted_types()}</span>
			<span class="font-sans text-xs text-ink-3 italic">{m.upload_filename_hint()}</span>
		</div>
	{/if}
</div>

{#if uploadMessages.length > 0}
	<div class="mb-4 flex flex-col gap-1">
		{#each uploadMessages as msg, i (i)}
			<p
				class="font-sans text-sm {msg.isError
					? 'text-red-600'
					: msg.link
						? 'text-amber-700'
						: 'text-green-700'}"
			>
				{msg.text}
				{#if msg.link}
					<a href={msg.link} class="underline hover:no-underline">{m.upload_duplicate_link()}</a>
				{/if}
			</p>
		{/each}
	</div>
{/if}

<input
	bind:this={fileInput}
	type="file"
	multiple
	accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff"
	aria-label={m.doc_file_upload_label()}
	class="sr-only"
	onchange={handleFileSelect}
/>
