<script lang="ts">
	const DEFAULT_ACCEPTED_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
	const DEFAULT_ACCEPT_ATTR = '.pdf,.jpg,.jpeg,.png';
	const DEFAULT_HINT = 'PDF, JPEG ou PNG — 10 Mo max par fichier';
	const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 Mo

	interface UploadedFile {
		id: string;
		filename: string;
		fileSize: number;
		mimeType: string;
	}

	let {
		existingFiles = [],
		onUpload,
		onDelete,
		disabled = false,
		acceptedTypes = DEFAULT_ACCEPTED_TYPES,
		acceptAttr = DEFAULT_ACCEPT_ATTR,
		hint = DEFAULT_HINT,
		showDelete = true
	}: {
		existingFiles?: UploadedFile[];
		onUpload: (file: File) => Promise<UploadedFile>;
		onDelete: (fileId: string) => Promise<void>;
		disabled?: boolean;
		acceptedTypes?: string[];
		acceptAttr?: string;
		hint?: string;
		showDelete?: boolean;
	} = $props();

	let files = $state<UploadedFile[]>(existingFiles);
	let pendingFiles = $state<{ name: string; size: number }[]>([]);
	let error = $state<string | null>(null);
	let isDragging = $state(false);
	let fileInput: HTMLInputElement;

	$effect(() => {
		files = existingFiles;
	});

	function formatFileSize(bytes: number): string {
		if (bytes < 1024) return `${bytes} o`;
		if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} Ko`;
		return `${(bytes / (1024 * 1024)).toFixed(1)} Mo`;
	}

	function getFileIcon(mimeType: string): string {
		if (mimeType === 'application/pdf') return '📄';
		if (mimeType.startsWith('image/')) return '🖼️';
		return '📎';
	}

	function formatFileType(mimeType: string): string {
		if (mimeType === 'application/pdf') return 'PDF';
		if (mimeType === 'image/jpeg') return 'JPEG';
		if (mimeType === 'image/png') return 'PNG';
		if (mimeType.includes('wordprocessingml')) return 'DOCX';
		const parts = mimeType.split('/');
		return parts[1]?.toUpperCase() ?? mimeType;
	}

	function validateFile(file: File): string | null {
		if (!acceptedTypes.includes(file.type)) {
			return `Type de fichier non accepté : ${file.type}.`;
		}
		if (file.size > MAX_FILE_SIZE) {
			return `Le fichier dépasse la taille maximale de 10 Mo (${formatFileSize(file.size)}).`;
		}
		return null;
	}

	async function processFiles(fileList: globalThis.FileList) {
		error = null;

		for (const file of fileList) {
			const validationError = validateFile(file);
			if (validationError) {
				error = validationError;
				continue;
			}

			pendingFiles = [...pendingFiles, { name: file.name, size: file.size }];

			try {
				const uploaded = await onUpload(file);
				files = [...files, uploaded];
			} catch {
				error = `Erreur lors de l'envoi de "${file.name}".`;
			} finally {
				pendingFiles = pendingFiles.filter((p) => p.name !== file.name);
			}
		}
	}

	async function handleFileSelect(event: Event) {
		const input = event.target as HTMLInputElement;
		const selectedFiles = input.files;
		if (!selectedFiles || selectedFiles.length === 0) return;

		await processFiles(selectedFiles);
		input.value = '';
	}

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

	function handleDragLeave(event: DragEvent) {
		const target = event.currentTarget as HTMLElement;
		const related = event.relatedTarget as globalThis.Node | null;
		if (related && target.contains(related)) return;
		isDragging = false;
	}

	async function handleDrop(event: DragEvent) {
		event.preventDefault();
		isDragging = false;
		const droppedFiles = event.dataTransfer?.files;
		if (!droppedFiles || droppedFiles.length === 0) return;
		await processFiles(droppedFiles);
	}

	async function handleDelete(fileId: string) {
		error = null;

		try {
			await onDelete(fileId);
			files = files.filter((f) => f.id !== fileId);
		} catch {
			error = 'Erreur lors de la suppression du fichier.';
		}
	}
</script>

<div class="file-upload" class:disabled>
	{#if error}
		<p class="upload-error" role="alert">{error}</p>
	{/if}

	{#if files.length > 0 || pendingFiles.length > 0}
		<ul class="file-list">
			{#each files as file}
				<li class="file-item">
					<span class="file-icon">{getFileIcon(file.mimeType)}</span>
					<span class="file-name">{file.filename}</span>
					<span class="file-type">{formatFileType(file.mimeType)}</span>
					<span class="file-size">{formatFileSize(file.fileSize)}</span>
					{#if !disabled && showDelete}
						<button
							type="button"
							class="file-remove"
							onclick={() => handleDelete(file.id)}
							title="Supprimer {file.filename}"
							aria-label="Supprimer {file.filename}"
						>
							✕
						</button>
					{/if}
				</li>
			{/each}
			{#each pendingFiles as pending}
				<li class="file-item file-pending">
					<span class="file-icon">⏳</span>
					<span class="file-name">{pending.name}</span>
					<span class="file-size">{formatFileSize(pending.size)}</span>
					<span class="file-uploading">Envoi...</span>
				</li>
			{/each}
		</ul>
	{/if}

	{#if !disabled}
		<!-- svelte-ignore a11y_no_static_element_interactions -->
		<div
			class="drop-zone"
			class:drop-zone-active={isDragging}
			ondragover={handleDragOver}
			ondragleave={handleDragLeave}
			ondrop={handleDrop}
			onclick={() => fileInput.click()}
		>
			<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
				<path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" />
				<polyline points="17 8 12 3 7 8" />
				<line x1="12" y1="3" x2="12" y2="15" />
			</svg>
			<p class="drop-zone-text">
				Glissez-déposez vos fichiers ici ou
				<button type="button" class="drop-zone-browse" onclick={(e) => { e.stopPropagation(); fileInput.click(); }}>
					parcourir
				</button>
			</p>
			<p class="upload-hint">{hint}</p>
		</div>
		<input
			bind:this={fileInput}
			type="file"
			accept={acceptAttr}
			onchange={handleFileSelect}
			multiple
			class="file-input-hidden"
			aria-hidden="true"
			tabindex="-1"
		/>
	{/if}
</div>

<style>
	.file-upload {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.file-upload.disabled {
		opacity: 0.6;
	}

	.upload-error {
		margin: 0;
		padding: 0.5rem 0.75rem;
		background: #fee2e2;
		border-radius: 0.375rem;
		color: #991b1b;
		font-size: 0.8125rem;
	}

	.file-list {
		list-style: none;
		margin: 0;
		padding: 0;
		display: flex;
		flex-direction: column;
		gap: 0.375rem;
	}

	.file-item {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.5rem 0.75rem;
		background: #f9fafb;
		border: 1px solid #e5e7eb;
		border-radius: 0.375rem;
		font-size: 0.875rem;
	}

	.file-pending {
		opacity: 0.6;
	}

	.file-icon {
		flex-shrink: 0;
	}

	.file-name {
		flex: 1;
		color: #374151;
		font-weight: 500;
		overflow: hidden;
		text-overflow: ellipsis;
		white-space: nowrap;
	}

	.file-type {
		color: #6b7280;
		font-size: 0.6875rem;
		font-weight: 500;
		padding: 0.0625rem 0.375rem;
		background: #f3f4f6;
		border-radius: 0.25rem;
		flex-shrink: 0;
	}

	.file-size {
		color: #9ca3af;
		font-size: 0.75rem;
		flex-shrink: 0;
	}

	.file-uploading {
		color: #3b82f6;
		font-size: 0.75rem;
		flex-shrink: 0;
	}

	.file-remove {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 1.5rem;
		height: 1.5rem;
		border: none;
		border-radius: 50%;
		background: #e5e7eb;
		color: #6b7280;
		font-size: 0.625rem;
		cursor: pointer;
		flex-shrink: 0;
		transition: background-color 0.15s;
	}

	.file-remove:hover {
		background: #fecaca;
		color: #dc2626;
	}

	.drop-zone {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 0.5rem;
		padding: 1.5rem;
		border: 2px dashed #d1d5db;
		border-radius: 0.5rem;
		background: #fafafa;
		color: #6b7280;
		cursor: pointer;
		transition: border-color 0.15s, background-color 0.15s;
	}

	.drop-zone:hover {
		border-color: #93c5fd;
		background: #eff6ff;
	}

	.drop-zone-active {
		border-color: #3b82f6;
		background: #dbeafe;
	}

	.drop-zone-text {
		margin: 0;
		font-size: 0.875rem;
		color: #6b7280;
		text-align: center;
	}

	.drop-zone-browse {
		display: inline;
		padding: 0;
		border: none;
		background: none;
		color: #2563eb;
		font-size: 0.875rem;
		font-weight: 500;
		cursor: pointer;
		text-decoration: underline;
	}

	.drop-zone-browse:hover {
		color: #1d4ed8;
	}

	.file-input-hidden {
		position: absolute;
		width: 0;
		height: 0;
		overflow: hidden;
		opacity: 0;
	}

	.upload-hint {
		margin: 0;
		font-size: 0.75rem;
		color: #9ca3af;
	}
</style>
