<script lang="ts">
	import {
		Download,
		GitBranch,
		ListChecks,
		Loader2,
		MoreHorizontal,
		Pencil,
		Pin,
		PinOff,
		Square,
		Trash2
	} from '@lucide/svelte';
	import { DropdownMenuActions } from '$lib/components/app';
	import { TruncatedText } from '$lib/components/app';
	import { Checkbox } from '$lib/components/ui/checkbox';
	import * as Tooltip from '$lib/components/ui/tooltip';
	import { FORK_TREE_DEPTH_PADDING, ICON_CLASS_DEFAULT } from '$lib/constants';
	import { RouterService } from '$lib/services/router.service';
	import { chatStore, conversationsStore } from '$lib/stores';
	import { onMount } from 'svelte';

	interface Props {
		isActive?: boolean;
		depth?: number;
		conversation: DatabaseConversation;
		isSelectionMode?: boolean;
		isSelected?: boolean;
		onDelete?: (id: string) => void;
		onEdit?: (id: string) => void;
		onSelect?: (id: string) => void;
		onStop?: (id: string) => void;
		onToggleSelect?: (id: string) => void;
		onEnterSelectionMode?: (id: string) => void;
		onSelectionClick?: (id: string, options: { shiftKey: boolean }) => void;
		onRowMouseDown?: (id: string, event: MouseEvent) => void;
	}

	let {
		conversation,
		depth = 0,
		isActive = false,
		isSelected = false,
		isSelectionMode = false,
		onDelete,
		onEdit,
		onEnterSelectionMode,
		onRowMouseDown,
		onSelect,
		onSelectionClick,
		onStop,
		onToggleSelect
	}: Props = $props();

	let renderActionsDropdown = $state(false);
	let dropdownOpen = $state(false);

	let isLoading = $derived(chatStore.getAllLoadingChats().includes(conversation.id));

	function handleEdit(event: Event) {
		event.stopPropagation();
		onEdit?.(conversation.id);
	}

	function handleDelete(event: Event) {
		event.stopPropagation();
		onDelete?.(conversation.id);
	}

	function handleStop(event: Event) {
		event.stopPropagation();
		onStop?.(conversation.id);
	}

	function handleTogglePin() {
		conversationsStore.toggleConversationPin(conversation.id);
	}

	function handleEnterSelectionMode(event: Event) {
		event.stopPropagation();
		onEnterSelectionMode?.(conversation.id);
	}

	function handleGlobalEditEvent(event: Event) {
		const customEvent = event as CustomEvent<{ conversationId: string }>;

		if (customEvent.detail.conversationId === conversation.id && isActive) {
			handleEdit(event);
		}
	}

	function handleMouseLeave() {
		if (!dropdownOpen) {
			renderActionsDropdown = false;
		}
	}

	function handleMouseOver() {
		if (isSelectionMode) return;

		renderActionsDropdown = true;
	}

	function handleSelect(event: MouseEvent) {
		if (isSelectionMode) {
			onSelectionClick?.(conversation.id, { shiftKey: event.shiftKey });
		} else {
			onSelect?.(conversation.id);
		}
	}

	function handleCheckboxClick(event: MouseEvent) {
		event.stopPropagation();

		if (isSelectionMode) {
			onSelectionClick?.(conversation.id, { shiftKey: event.shiftKey });
		} else {
			onToggleSelect?.(conversation.id);
		}
	}

	function handleRowMouseDown(event: MouseEvent) {
		onRowMouseDown?.(conversation.id, event);
	}

	function handleCheckboxKeydown(event: KeyboardEvent) {
		if (event.key !== ' ' && event.key !== 'Enter') return;

		event.stopPropagation();
		event.preventDefault();

		if (isSelectionMode) {
			onSelectionClick?.(conversation.id, { shiftKey: event.shiftKey });
		} else {
			onToggleSelect?.(conversation.id);
		}
	}

	$effect(() => {
		if (!dropdownOpen) {
			renderActionsDropdown = false;
		}
	});

	onMount(() => {
		document.addEventListener('edit-active-conversation', handleGlobalEditEvent as EventListener);

		return () => {
			document.removeEventListener(
				'edit-active-conversation',
				handleGlobalEditEvent as EventListener
			);
		};
	});
</script>

<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<button
	class="group flex min-h-9 w-full cursor-pointer items-center justify-between space-x-3 rounded-lg py-1.5 text-left transition-colors hover:bg-foreground/10 {isActive
		? 'bg-foreground/5 text-accent-foreground'
		: ''} {isSelected ? 'bg-primary/10 hover:bg-primary/15' : ''} {isSelectionMode
		? 'is-selection-mode'
		: ''} px-2"
	data-conversation-row={conversation.id}
	onclick={(e) => handleSelect(e)}
	onmouseover={handleMouseOver}
	onmouseleave={handleMouseLeave}
	onmousedown={(e) => handleRowMouseDown(e)}
	onfocusin={handleMouseOver}
	onfocusout={(e) => {
		if (!e.currentTarget.contains(e.relatedTarget as Node | null)) {
			handleMouseLeave();
		}
	}}
>
	<div
		class="flex min-w-0 flex-1 items-center gap-2"
		style:padding-left="{depth * FORK_TREE_DEPTH_PADDING}px"
	>
		{#if isSelectionMode}
			<div
				class="shrink-0"
				onclick={(e) => handleCheckboxClick(e)}
				onkeydown={handleCheckboxKeydown}
				role="checkbox"
				aria-checked={isSelected}
				aria-label={isSelected ? `Deselect ${conversation.name}` : `Select ${conversation.name}`}
				tabindex="-1"
			>
				<Checkbox
					checked={isSelected}
					aria-label={isSelected ? `Deselect ${conversation.name}` : `Select ${conversation.name}`}
				/>
			</div>
		{/if}

		{#if depth > 0}
			<Tooltip.Root>
				<Tooltip.Trigger>
					<!-- prevent another nested button element -->
					{#snippet child({ props })}
						<a
							{...props}
							href={RouterService.chat(conversation.forkedFromConversationId)}
							class="flex shrink-0 items-center text-muted-foreground transition-colors hover:text-foreground"
						>
							<GitBranch class="h-3.5 w-3.5" />
						</a>
					{/snippet}
				</Tooltip.Trigger>

				<Tooltip.Content>
					<p>See parent conversation</p>
				</Tooltip.Content>
			</Tooltip.Root>
		{/if}

		{#if isLoading}
			<Tooltip.Root>
				<Tooltip.Trigger>
					<div
						class="stop-button flex {ICON_CLASS_DEFAULT} shrink-0 cursor-pointer items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground"
						onclick={handleStop}
						onkeydown={(e) => e.key === 'Enter' && handleStop(e)}
						role="button"
						tabindex="0"
						aria-label="Stop generation"
					>
						<Loader2 class="loading-icon h-3.5 w-3.5 animate-spin" />

						<Square class="stop-icon hidden h-3 w-3 fill-current text-destructive" />
					</div>
				</Tooltip.Trigger>

				<Tooltip.Content>
					<p>Stop generation</p>
				</Tooltip.Content>
			</Tooltip.Root>
		{/if}

		<TruncatedText text={conversation.name} class="text-sm font-medium" showTooltip={false} />
	</div>

	{#if !isSelectionMode && renderActionsDropdown}
		<div class="actions flex items-center">
			<DropdownMenuActions
				triggerIcon={MoreHorizontal}
				triggerTooltip="More actions"
				bind:open={dropdownOpen}
				actions={[
					{
						icon: conversation.pinned ? PinOff : Pin,
						label: conversation.pinned ? 'Unpin' : 'Pin',
						onclick: (e: Event) => {
							e.stopPropagation();
							handleTogglePin();
						}
					},
					{
						icon: Pencil,
						label: 'Edit',
						onclick: handleEdit,
						shortcut: ['shift', 'cmd', 'e']
					},
					{
						icon: Download,
						label: 'Export',
						onclick: (e: Event) => {
							e.stopPropagation();
							conversationsStore.downloadConversation(conversation.id);
						},
						shortcut: ['shift', 'cmd', 's']
					},
					{
						icon: ListChecks,
						label: 'Select',
						onclick: handleEnterSelectionMode
					},
					{
						icon: Trash2,
						label: 'Delete',
						onclick: handleDelete,
						separator: true,
						shortcut: ['shift', 'cmd', 'd'],
						variant: 'destructive'
					}
				]}
			/>
		</div>
	{/if}
</button>

<style>
	button {
		:global([data-slot='dropdown-menu-trigger']:not([data-state='open'])) {
			opacity: 0;
		}

		&:is(:hover) :global([data-slot='dropdown-menu-trigger']),
		&:focus-within :global([data-slot='dropdown-menu-trigger']) {
			opacity: 1;
		}
		@media (max-width: 768px) {
			:global([data-slot='dropdown-menu-trigger']) {
				opacity: 1 !important;
			}
		}

		&.is-selection-mode :global([data-slot='dropdown-menu-trigger']) {
			display: none !important;
		}

		.stop-button {
			:global(.stop-icon) {
				display: none;
			}

			:global(.loading-icon) {
				display: block;
			}
		}

		&:is(:hover) .stop-button {
			:global(.stop-icon) {
				display: block;
			}

			:global(.loading-icon) {
				display: none;
			}
		}
	}
</style>
