<!--
	Move/copy every photo in a heap into a folder under originals/.

	Picker reuses the existing FolderTree in readonly mode; the dialog owns
	the selection (`pickedPath`) so it doesn't conflict with the global
	folderPath filter the sidebar drives.

	Submit goes to the sidecar's POST /albums/:uid/convert. On success we
	invalidate the photos / folders / heaps queries so the timeline and
	sidebar refresh; if the heap was deleted and was active, route home.
-->
<script lang="ts">
	import { goto } from '$app/navigation';
	import { Dialog } from 'bits-ui';
	import { createMutation, createQuery, useQueryClient } from '@tanstack/svelte-query';
	import { toast } from 'svelte-sonner';
	import { FolderInput, Loader2 } from 'lucide-svelte';
	import {
		convertHeap,
		listFolders,
		type HeapConvertBody,
		type HeapConvertResult,
		type PpAlbum,
		type PpFolder
	} from '$lib/services/photoprism';
	import { filters, setSection } from '$lib/stores/filters.svelte';
	import { isAuthenticated } from '$lib/stores/session.svelte';
	import FolderTree, { buildTree } from './FolderTree.svelte';

	interface Props {
		heap: PpAlbum | null;
		onClose: () => void;
	}
	let { heap, onClose }: Props = $props();

	const qc = useQueryClient();

	// Reuse the same folders cache the sidebar uses — same key so we share
	// the in-flight request, and the picker invalidates it on success.
	const foldersQuery = createQuery<PpFolder[]>(() => ({
		queryKey: ['folders'],
		queryFn: listFolders,
		enabled: isAuthenticated()
	}));
	const folderTree = $derived(
		buildTree((foldersQuery.data ?? []).map((f) => f.Path))
	);

	let pickedPath = $state<string | null>(null);
	let mode = $state<'move' | 'copy'>('move');
	let subfolder = $state('');
	let deleteHeap = $state(false);

	// Reset draft state whenever a new heap is picked (or the dialog closes
	// and reopens). $effect runs after the prop change, so the form is
	// blank on every fresh open.
	$effect(() => {
		void heap;
		pickedPath = null;
		mode = 'move';
		subfolder = '';
		deleteHeap = false;
	});

	const convertMut = createMutation(() => ({
		mutationFn: (args: { uid: string; body: HeapConvertBody }) =>
			convertHeap(args.uid, args.body),
		onSuccess: (result: HeapConvertResult, vars) => {
			qc.invalidateQueries({ queryKey: ['photos'] });
			qc.invalidateQueries({ queryKey: ['folders'] });
			qc.invalidateQueries({ queryKey: ['heaps'] });
			const verb = mode === 'copy' ? 'Copied' : 'Moved';
			const count = mode === 'copy' ? result.copied : result.moved;
			const tail =
				result.errors.length > 0
					? ` · ${result.errors.length} skipped`
					: '';
			toast.success(`${verb} ${count} photo${count === 1 ? '' : 's'}${tail}`);
			// If the heap got deleted and we were viewing it, fall back home.
			if (
				result.heap_deleted &&
				filters.section === 'heap' &&
				filters.heapUid === vars.uid
			) {
				setSection('all-photos');
				void goto('/', { keepFocus: true, noScroll: true });
			}
			onClose();
		},
		onError: (err) =>
			toast.error(err instanceof Error ? err.message : 'Convert failed')
	}));

	function submit() {
		if (!heap || !pickedPath) return;
		convertMut.mutate({
			uid: heap.UID,
			body: {
				targetFolder: pickedPath,
				mode,
				subfolder: subfolder.trim() || null,
				deleteHeap: mode === 'move' && deleteHeap
			}
		});
	}

	// Copy mode doesn't change membership, so "delete heap after" is
	// meaningless. Force-clear it when the user flips back to copy.
	$effect(() => {
		if (mode === 'copy' && deleteHeap) deleteHeap = false;
	});

	const open = $derived(heap !== null);
</script>

<Dialog.Root
	{open}
	onOpenChange={(o) => {
		if (!o) onClose();
	}}
>
	<Dialog.Portal>
		<Dialog.Overlay
			class="fixed inset-0 z-40 bg-background/80 backdrop-blur-sm data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0"
		/>
		<Dialog.Content
			class="fixed left-1/2 top-1/2 z-50 grid w-full max-w-[520px] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border border-border bg-card p-5 text-card-foreground shadow-lg outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95"
		>
			<div class="flex items-start gap-2">
				<FolderInput class="mt-0.5 h-4 w-4 text-muted-foreground" />
				<div class="flex-1">
					<Dialog.Title class="text-sm font-semibold leading-tight">
						{mode === 'copy' ? 'Copy' : 'Move'} heap to folder
					</Dialog.Title>
					<Dialog.Description class="mt-1 text-xs text-muted-foreground">
						{heap?.Title ?? ''} · {heap?.PhotoCount ?? 0} photo{heap?.PhotoCount === 1
							? ''
							: 's'}
					</Dialog.Description>
				</div>
			</div>

			<!-- Folder picker. Readonly FolderTree so the user can't kebab/
			     rename their way out of the picker mid-flow. -->
			<div class="rounded-md border border-border bg-background p-2">
				<div class="mb-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">
					Destination
				</div>
				<div class="max-h-[200px] overflow-y-auto">
					{#if foldersQuery.isPending}
						<p class="px-2 py-1 text-[11px] text-muted-foreground">Loading folders…</p>
					{:else if (foldersQuery.data ?? []).length === 0}
						<p class="px-2 py-1 text-[11px] text-muted-foreground">
							No folders. Create one from the sidebar first.
						</p>
					{:else}
						<FolderTree
							nodes={folderTree}
							onPick={(p) => (pickedPath = p)}
							selectedPath={pickedPath}
							readonly
						/>
					{/if}
				</div>
			</div>

			<!-- Mode + options. Plain radio + checkbox; bits-ui has dedicated
			     primitives but inline form controls keep the dialog small. -->
			<div class="space-y-2">
				<div class="flex items-center gap-4 text-[12px]">
					<label class="flex items-center gap-1.5">
						<input type="radio" bind:group={mode} value="move" />
						Move
					</label>
					<label class="flex items-center gap-1.5">
						<input type="radio" bind:group={mode} value="copy" />
						Copy
					</label>
				</div>
				<label class="flex flex-col gap-1 text-[12px]">
					<span class="text-muted-foreground">
						New subfolder (optional)
					</span>
					<input
						type="text"
						placeholder="e.g. {heap?.Title ?? 'My heap'}"
						bind:value={subfolder}
						class="rounded border border-input bg-background px-2 py-1 text-[12px] focus:outline-none focus:ring-2 focus:ring-ring"
					/>
				</label>
				<label class="flex items-center gap-1.5 text-[12px]">
					<input
						type="checkbox"
						bind:checked={deleteHeap}
						disabled={mode === 'copy'}
					/>
					<span class:text-muted-foreground={mode === 'copy'}>
						Delete heap after move
					</span>
				</label>
			</div>

			<div class="flex items-center justify-end gap-2 pt-1">
				<button
					type="button"
					class="rounded border border-border px-3 py-1 text-[12px] hover:bg-accent"
					onclick={onClose}
					disabled={convertMut.isPending}
				>
					Cancel
				</button>
				<button
					type="button"
					class="flex items-center gap-1.5 rounded bg-primary px-3 py-1 text-[12px] text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
					onclick={submit}
					disabled={!pickedPath || convertMut.isPending}
				>
					{#if convertMut.isPending}
						<Loader2 class="h-3 w-3 animate-spin" />
					{/if}
					{mode === 'copy' ? 'Copy' : 'Move'}
				</button>
			</div>
		</Dialog.Content>
	</Dialog.Portal>
</Dialog.Root>
