<script lang="ts">
	import { AlertDialog, Button } from 'bits-ui';
	import { GitBranch, Star, Clock, Import, Search, RefreshCw, LoaderCircle } from '@lucide/svelte';
	import type { Repository } from './types/repo';
	import { createQuery } from '@tanstack/svelte-query';
	import { authClient } from './auth-client';
	import { apiClient } from './api-client';
	import NProgress from 'nprogress';
	import { toast } from 'svelte-sonner';
	import { formatDistanceToNow } from 'date-fns';

	const session = authClient.useSession();

	const query = createQuery(() => ({
		queryKey: ['recent-repositories'],
		queryFn: async () => {
			return await apiClient.request<Repository[]>(`/api/v0/user/repos`);
		},
		enabled: !!$session.data?.user.id,
		staleTime: 30000
	}));

	let searchQuery = $state('');
	let throttledQuery = $state('');

	$effect(() => {
		const value = searchQuery;
		const t = setTimeout(() => {
			throttledQuery = value;
		}, 300);
		return () => clearTimeout(t);
	});

	$effect(() => {
		if (searchResultsQuery.isFetching || query.isFetching) {
			NProgress.start();
		} else {
			NProgress.done();
		}
	});

	const searchResultsQuery = createQuery(() => ({
		queryKey: ['search-repositories', throttledQuery],
		queryFn: async () => {
			return await apiClient.request<Repository[]>(
				`/api/v0/user/repos/search?q=${encodeURIComponent(throttledQuery)}`
			);
		},
		enabled: !!$session.data?.user.id && throttledQuery.length > 0
	}));

	let adding = $state<number | null>(null);
	let added = $state<number[]>([]);
	$effect(() => {
		added = repos.filter((r) => r.added).map((r) => r.id);
	});

	const searching = $derived(throttledQuery.length > 0);
	const repos = $derived(searching ? (searchResultsQuery.data ?? []) : (query.data ?? []));
	const isPending = $derived(searching ? searchResultsQuery.isPending : query.isPending);

	let dialogOpen = $state(false);
	let selectedRepo = $state<Repository | null>(null);
	let description = $state('');

	const openDialog = (repo: Repository) => {
		if (added.includes(repo.id)) {
			toast.warning('Repository already imported');
			return;
		}
		selectedRepo = repo;
		description = repo.description ?? '';
		dialogOpen = true;
	};

	const handleImport = async () => {
		if (!selectedRepo) return;
		adding = selectedRepo.id;
		dialogOpen = false;

		let response = await fetch('/api/v0/user/repo/add', {
			method: 'POST',
			headers: {
				'Content-Type': 'application/json',
				Authorization: (await apiClient.getToken()) ?? ''
			},
			body: JSON.stringify({
				id: selectedRepo.id.toString(),
				full_name: selectedRepo.full_name,
				description
			})
		});
		const data = await response.json();
		if (!response.ok) {
			toast.warning(data.error);
			adding = null;
			return;
		} else {
			toast.success('Successfully added repository');
		}
		added.push(selectedRepo.id);
		adding = null;
		selectedRepo = null;
		description = '';
	};

	const languageColors: Record<string, string> = {
		GDScript: 'bg-lang-gdscript',
		Rust: 'bg-lang-rust'
	};
</script>

<div class="rounded-xl bg-surface p-6 shadow-xl ring-1 ring-border">
	<div class="mb-6 flex items-center justify-between">
		<div class="flex items-center gap-3">
			<GitBranch class="h-6 w-6 text-icon" />
			<h2 class="text-xl font-semibold text-text-primary">Import Git Repository</h2>
		</div>
		<Button.Root
			class="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
			onclick={() => (searching ? searchResultsQuery.refetch() : query.refetch())}
		>
			<RefreshCw class="h-4 w-4" />
			<span>Refresh</span>
		</Button.Root>
	</div>

	<div class="relative mb-6">
		<Search class="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-icon-muted" />
		<input
			type="text"
			bind:value={searchQuery}
			placeholder="Search repositories..."
			class="w-full rounded-lg border border-border-hover bg-surface-hover py-2.5 pr-4 pl-10 text-text-primary placeholder-text-muted transition-colors outline-none focus:border-border-focus focus:ring-1 focus:ring-border-focus"
		/>
	</div>

	<div class="space-y-3">
		{#each repos as repo (repo.id)}
			<div
				class="group flex items-center justify-between rounded-lg border border-border bg-surface-muted p-4 transition-colors hover:border-border-hover hover:bg-surface-hover"
			>
				<div class="flex-1">
					<div class="flex items-center gap-2">
						<h3 class="truncate font-medium text-text-primary">{repo.name}</h3>
					</div>
					{#if repo.description}
						<p class="mt-1 truncate text-sm text-text-secondary">{repo.description}</p>
					{:else}
						<p class="mt-1 text-sm text-text-muted italic">No description</p>
					{/if}
					<div class="mt-2 flex items-center gap-4 text-xs text-text-muted">
						{#if repo.language}
							<span class="flex items-center gap-1.5">
								<span
									class="h-2.5 w-2.5 rounded-full {languageColors[repo.language] ??
										'bg-lang-default'}"
								></span>
								{repo.language}
							</span>
						{/if}
						<span class="flex items-center gap-1">
							<Star class="h-3.5 w-3.5" />
							{repo.stars ?? 0}
						</span>
						<span class="flex items-center gap-1">
							<Clock class="h-3.5 w-3.5" />
							{formatDistanceToNow(new Date(repo.updated_at), { addSuffix: true })}
						</span>
					</div>
				</div>
				<Button.Root
					class="ml-4 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-text-primary transition-colors {added.includes(
						repo.id
					)
						? 'border-success-border bg-success'
						: 'bg-primary'} disabled:cursor-default disabled:opacity-50"
					disabled={adding !== null}
					onclick={() => openDialog(repo)}
				>
					{#if adding === repo.id}
						<RefreshCw class="h-4 w-4 animate-spin" />
						<span>Adding...</span>
					{:else}
						<Import class="h-4 w-4" />
						<span>{added.includes(repo.id) ? 'Added' : 'Add'}</span>
					{/if}
				</Button.Root>
			</div>
		{:else}
			{#if isPending}
				<div class="flex justify-center">
					<LoaderCircle class="animate-spin" />
				</div>
			{:else}
				<div class="py-8 text-center text-text-muted">
					<p>No repositories found matching "{searchQuery}"</p>
				</div>
			{/if}
		{/each}
	</div>

	<p class="mt-6 text-center text-sm text-text-muted">
		You must have a dist/ folder with index.html + index.wasm
	</p>
</div>

<AlertDialog.Root bind:open={dialogOpen}>
	<AlertDialog.Portal>
		<AlertDialog.Overlay class="fixed inset-0 z-50 bg-black/80" />
		<AlertDialog.Content
			class="fixed top-1/2 left-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-surface p-6 shadow-xl"
		>
			<div class="flex flex-col gap-4">
				<AlertDialog.Title class="text-lg font-semibold text-text-primary">
					Add Repository
				</AlertDialog.Title>
				<AlertDialog.Description class="text-sm text-text-secondary">
					{#if selectedRepo}
						You are about to add <span class="font-semibold text-text-primary"
							>{selectedRepo.full_name}</span
						>. Please provide a description for your project.
					{/if}
				</AlertDialog.Description>

				<div class="mt-2">
					<label for="description" class="mb-2 block text-sm font-medium text-text-primary">
						Description
					</label>
					<textarea
						id="description"
						bind:value={description}
						placeholder="Describe your project..."
						rows="3"
						class="w-full rounded-lg border border-border-hover bg-surface-hover px-3 py-2 text-text-primary placeholder-text-muted transition-colors outline-none focus:border-border-focus focus:ring-1 focus:ring-border-focus"
					></textarea>
				</div>
			</div>

			<div class="mt-6 flex justify-end gap-3">
				<AlertDialog.Cancel
					class="rounded-lg border border-border px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:bg-surface-hover hover:text-text-primary"
				>
					Cancel
				</AlertDialog.Cancel>
				<AlertDialog.Action
					class="rounded-lg bg-primary px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-primary/90"
					onclick={handleImport}
				>
					Continue
				</AlertDialog.Action>
			</div>
		</AlertDialog.Content>
	</AlertDialog.Portal>
</AlertDialog.Root>
