1<script lang="ts">
  2	import { Search, X } from '@lucide/svelte';
  3	import { Button } from '$lib/components/ui/button';
  4	import { Input } from '$lib/components/ui/input';
  5	import { Checkbox } from '$lib/components/ui/checkbox';
  6	import { ScrollArea } from '$lib/components/ui/scroll-area';
  7	import { SvelteSet } from 'svelte/reactivity';
  8
  9	interface Props {
 10		conversations: DatabaseConversation[];
 11		messageCountMap?: Map<string, number>;
 12		mode: 'export' | 'import';
 13		onCancel: () => void;
 14		onConfirm: (selectedConversations: DatabaseConversation[]) => void;
 15	}
 16
 17	let { conversations, messageCountMap = new Map(), mode, onCancel, onConfirm }: Props = $props();
 18
 19	let searchQuery = $state('');
 20	let selectedIds = $state.raw<SvelteSet<string>>(new SvelteSet(conversations.map((c) => c.id)));
 21	let lastClickedId = $state<string | null>(null);
 22
 23	let filteredConversations = $derived(
 24		conversations.filter((conv) => {
 25			const name = conv.name || 'Untitled conversation';
 26			return name.toLowerCase().includes(searchQuery.toLowerCase());
 27		})
 28	);
 29
 30	let allSelected = $derived(
 31		filteredConversations.length > 0 &&
 32			filteredConversations.every((conv) => selectedIds.has(conv.id))
 33	);
 34
 35	let someSelected = $derived(
 36		filteredConversations.some((conv) => selectedIds.has(conv.id)) && !allSelected
 37	);
 38
 39	function toggleConversation(id: string, shiftKey: boolean = false) {
 40		const newSet = new SvelteSet(selectedIds);
 41
 42		if (shiftKey && lastClickedId !== null) {
 43			const lastIndex = filteredConversations.findIndex((c) => c.id === lastClickedId);
 44			const currentIndex = filteredConversations.findIndex((c) => c.id === id);
 45
 46			if (lastIndex !== -1 && currentIndex !== -1) {
 47				const start = Math.min(lastIndex, currentIndex);
 48				const end = Math.max(lastIndex, currentIndex);
 49
 50				const shouldSelect = !newSet.has(id);
 51
 52				for (let i = start; i <= end; i++) {
 53					if (shouldSelect) {
 54						newSet.add(filteredConversations[i].id);
 55					} else {
 56						newSet.delete(filteredConversations[i].id);
 57					}
 58				}
 59
 60				selectedIds = newSet;
 61				return;
 62			}
 63		}
 64
 65		if (newSet.has(id)) {
 66			newSet.delete(id);
 67		} else {
 68			newSet.add(id);
 69		}
 70
 71		selectedIds = newSet;
 72		lastClickedId = id;
 73	}
 74
 75	function toggleAll() {
 76		if (allSelected) {
 77			const newSet = new SvelteSet(selectedIds);
 78
 79			filteredConversations.forEach((conv) => newSet.delete(conv.id));
 80			selectedIds = newSet;
 81		} else {
 82			const newSet = new SvelteSet(selectedIds);
 83
 84			filteredConversations.forEach((conv) => newSet.add(conv.id));
 85			selectedIds = newSet;
 86		}
 87	}
 88
 89	function handleConfirm() {
 90		const selected = conversations.filter((conv) => selectedIds.has(conv.id));
 91		onConfirm(selected);
 92	}
 93
 94	function handleCancel() {
 95		selectedIds = new SvelteSet(conversations.map((c) => c.id));
 96		searchQuery = '';
 97		lastClickedId = null;
 98
 99		onCancel();
100	}
101
102	export function reset() {
103		selectedIds = new SvelteSet(conversations.map((c) => c.id));
104		searchQuery = '';
105		lastClickedId = null;
106	}
107</script>
108
109<div class="space-y-4">
110	<div class="relative">
111		<Search class="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
112
113		<Input bind:value={searchQuery} placeholder="Search conversations..." class="pr-9 pl-9" />
114
115		{#if searchQuery}
116			<button
117				class="absolute top-1/2 right-3 -translate-y-1/2 text-muted-foreground hover:text-foreground"
118				onclick={() => (searchQuery = '')}
119				type="button"
120			>
121				<X class="h-4 w-4" />
122			</button>
123		{/if}
124	</div>
125
126	<div class="flex items-center justify-between text-sm text-muted-foreground">
127		<span>
128			{selectedIds.size} of {conversations.length} selected
129			{#if searchQuery}
130				({filteredConversations.length} shown)
131			{/if}
132		</span>
133	</div>
134
135	<div class="overflow-hidden rounded-md border">
136		<ScrollArea class="h-[400px]">
137			<table class="w-full">
138				<thead class="sticky top-0 z-10 bg-muted">
139					<tr class="border-b">
140						<th class="w-12 p-3 text-left">
141							<Checkbox
142								checked={allSelected}
143								indeterminate={someSelected}
144								onCheckedChange={toggleAll}
145							/>
146						</th>
147
148						<th class="p-3 text-left text-sm font-medium">Conversation Name</th>
149
150						<th class="w-32 p-3 text-left text-sm font-medium">Messages</th>
151					</tr>
152				</thead>
153				<tbody>
154					{#if filteredConversations.length === 0}
155						<tr>
156							<td colspan="3" class="p-8 text-center text-sm text-muted-foreground">
157								{#if searchQuery}
158									No conversations found matching "{searchQuery}"
159								{:else}
160									No conversations available
161								{/if}
162							</td>
163						</tr>
164					{:else}
165						{#each filteredConversations as conv (conv.id)}
166							<tr
167								class="cursor-pointer border-b transition-colors hover:bg-muted/50"
168								onclick={(e) => toggleConversation(conv.id, e.shiftKey)}
169							>
170								<td class="p-3">
171									<Checkbox
172										checked={selectedIds.has(conv.id)}
173										onclick={(e) => {
174											e.preventDefault();
175											e.stopPropagation();
176											toggleConversation(conv.id, e.shiftKey);
177										}}
178									/>
179								</td>
180
181								<td class="p-3 text-sm">
182									<div class="max-w-[17rem] truncate" title={conv.name || 'Untitled conversation'}>
183										{conv.name || 'Untitled conversation'}
184									</div>
185								</td>
186
187								<td class="p-3 text-sm text-muted-foreground">
188									{messageCountMap.get(conv.id) ?? 0}
189								</td>
190							</tr>
191						{/each}
192					{/if}
193				</tbody>
194			</table>
195		</ScrollArea>
196	</div>
197
198	<div class="flex justify-end gap-2">
199		<Button variant="outline" onclick={handleCancel}>Cancel</Button>
200
201		<Button onclick={handleConfirm} disabled={selectedIds.size === 0}>
202			{mode === 'export' ? 'Export' : 'Import'} ({selectedIds.size})
203		</Button>
204	</div>
205</div>