<script lang="ts">
	import Modal from '$lib/components/common/Modal.svelte';
	import { WEBUI_API_BASE_URL } from '$lib/constants';

	export let show = false;

	type WorkspaceEntry = {
		name: string;
		path: string;
		kind: 'file' | 'directory' | 'symlink';
		size: number;
		modified_at: number;
	};

	let path = '.';
	let entries: WorkspaceEntry[] = [];
	let loading = false;
	let downloading = '';
	let error = '';
	let opened = false;

	$: pathParts = path === '.' ? [] : path.split('/');
	$: breadcrumbs = [
		{ label: '工作区', path: '.' },
		...pathParts.map((part, index) => ({
			label: part,
			path: pathParts.slice(0, index + 1).join('/')
		}))
	];

	$: if (show && !opened) {
		opened = true;
		void loadFiles(path);
	}
	$: if (!show) opened = false;

	const authHeaders = () => ({
		Authorization: `Bearer ${localStorage.token}`
	});

	async function responseError(response: Response): Promise<string> {
		try {
			const payload = await response.json();
			return typeof payload?.detail === 'string'
				? payload.detail
				: JSON.stringify(payload?.detail ?? payload);
		} catch {
			return `请求失败（${response.status}）`;
		}
	}

	async function loadFiles(nextPath: string) {
		loading = true;
		error = '';
		try {
			const response = await fetch(
				`${WEBUI_API_BASE_URL}/k1412/workspace/files?path=${encodeURIComponent(nextPath)}`,
				{ headers: authHeaders() }
			);
			if (!response.ok) throw new Error(await responseError(response));
			const payload = await response.json();
			path = payload.path || '.';
			entries = payload.entries || [];
		} catch (exception) {
			error = exception instanceof Error ? exception.message : String(exception);
		} finally {
			loading = false;
		}
	}

	function downloadName(response: Response, fallback: string): string {
		const disposition = response.headers.get('content-disposition') ?? '';
		const encoded = disposition.match(/filename\*=UTF-8''([^;]+)/i)?.[1];
		if (encoded) {
			try {
				return decodeURIComponent(encoded);
			} catch {
				return fallback;
			}
		}
		return fallback;
	}

	async function saveDownload(
		endpoint: 'download' | 'archive',
		itemPath: string,
		fallback: string
	) {
		downloading = itemPath;
		error = '';
		try {
			const response = await fetch(
				`${WEBUI_API_BASE_URL}/k1412/workspace/${endpoint}?path=${encodeURIComponent(itemPath)}`,
				{ headers: authHeaders() }
			);
			if (!response.ok) throw new Error(await responseError(response));
			const blob = await response.blob();
			const url = URL.createObjectURL(blob);
			const link = document.createElement('a');
			link.href = url;
			link.download = downloadName(response, fallback);
			document.body.appendChild(link);
			link.click();
			link.remove();
			URL.revokeObjectURL(url);
		} catch (exception) {
			error = exception instanceof Error ? exception.message : String(exception);
		} finally {
			downloading = '';
		}
	}

	function formatSize(size: number): string {
		if (size < 1024) return `${size} B`;
		if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
		if (size < 1024 * 1024 * 1024) return `${(size / 1024 / 1024).toFixed(1)} MB`;
		return `${(size / 1024 / 1024 / 1024).toFixed(1)} GB`;
	}
</script>

<Modal bind:show size="lg" className="bg-white dark:bg-gray-900 rounded-3xl overflow-hidden">
	<div class="flex h-[min(74vh,46rem)] min-h-[28rem] flex-col">
		<header
			class="flex items-center justify-between gap-3 border-b border-gray-100 px-5 py-4 dark:border-gray-800"
		>
			<div>
				<h2 class="text-base font-semibold text-gray-900 dark:text-white">工作区文件</h2>
				<p class="mt-0.5 text-xs text-gray-500">这里是当前用户 Agent 生成和修改的文件</p>
			</div>
			<div class="flex items-center gap-2">
				<button
					type="button"
					class="rounded-xl border border-gray-200 px-3 py-2 text-xs font-medium text-gray-700 transition hover:bg-gray-50 disabled:opacity-50 dark:border-gray-700 dark:text-gray-200 dark:hover:bg-gray-800"
					on:click={() =>
						saveDownload(
							'archive',
							path,
							path === '.' ? 'workspace.tar.gz' : `${pathParts.at(-1)}.tar.gz`
						)}
					disabled={loading || Boolean(downloading)}
				>
					{downloading === path ? '打包中…' : '打包下载'}
				</button>
				<button
					type="button"
					class="rounded-xl p-2 text-gray-500 transition hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-white"
					on:click={() => (show = false)}
					aria-label="关闭"
				>
					<svg
						viewBox="0 0 24 24"
						class="size-5"
						fill="none"
						stroke="currentColor"
						stroke-width="1.8"
					>
						<path d="M6 6l12 12M18 6 6 18" stroke-linecap="round" />
					</svg>
				</button>
			</div>
		</header>

		<nav
			class="flex min-h-12 items-center gap-1 overflow-x-auto border-b border-gray-100 px-5 dark:border-gray-800"
		>
			{#each breadcrumbs as crumb, index}
				{#if index > 0}<span class="text-gray-300 dark:text-gray-700">/</span>{/if}
				<button
					type="button"
					class="shrink-0 rounded-lg px-2 py-1 text-xs {index === breadcrumbs.length - 1
						? 'font-medium text-gray-900 dark:text-white'
						: 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'}"
					on:click={() => loadFiles(crumb.path)}
				>
					{crumb.label}
				</button>
			{/each}
			<button
				type="button"
				class="ml-auto shrink-0 rounded-lg p-2 text-gray-400 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
				on:click={() => loadFiles(path)}
				aria-label="刷新"
			>
				<svg
					viewBox="0 0 24 24"
					class="size-4"
					fill="none"
					stroke="currentColor"
					stroke-width="1.8"
				>
					<path
						d="M20 11a8 8 0 1 0-2.34 5.66M20 4v7h-7"
						stroke-linecap="round"
						stroke-linejoin="round"
					/>
				</svg>
			</button>
		</nav>

		<div class="min-h-0 flex-1 overflow-y-auto px-3 py-3">
			{#if error}
				<div
					class="mx-2 mb-3 rounded-xl bg-red-50 px-3 py-2 text-sm text-red-700 dark:bg-red-950/40 dark:text-red-300"
				>
					{error}
				</div>
			{/if}
			{#if loading}
				<div class="flex h-40 items-center justify-center text-sm text-gray-400">
					正在读取工作区…
				</div>
			{:else if entries.length === 0}
				<div class="flex h-40 flex-col items-center justify-center text-gray-400">
					<svg
						viewBox="0 0 24 24"
						class="mb-3 size-9"
						fill="none"
						stroke="currentColor"
						stroke-width="1.5"
					>
						<path d="M3.5 6.5h6l2 2h9v10h-17z" stroke-linejoin="round" />
					</svg>
					<span class="text-sm">这个目录还是空的</span>
				</div>
			{:else}
				<div class="space-y-1">
					{#each entries as entry}
						<div
							class="group flex items-center gap-3 rounded-xl px-3 py-2.5 transition hover:bg-gray-50 dark:hover:bg-gray-800/70"
						>
							<div class={entry.kind === 'directory' ? 'text-amber-500' : 'text-gray-400'}>
								{#if entry.kind === 'directory'}
									<svg viewBox="0 0 24 24" class="size-5" fill="currentColor">
										<path d="M3 5.5h7l2 2h9v11H3z" />
									</svg>
								{:else}
									<svg
										viewBox="0 0 24 24"
										class="size-5"
										fill="none"
										stroke="currentColor"
										stroke-width="1.7"
									>
										<path d="M6 2.8h8l4 4v14.4H6zM14 2.8v4h4" stroke-linejoin="round" />
									</svg>
								{/if}
							</div>
							<button
								type="button"
								class="min-w-0 flex-1 text-left"
								on:click={() =>
									entry.kind === 'directory'
										? loadFiles(entry.path)
										: saveDownload('download', entry.path, entry.name)}
								disabled={entry.kind === 'symlink'}
							>
								<div class="truncate text-sm font-medium text-gray-800 dark:text-gray-100">
									{entry.name}
								</div>
								<div class="mt-0.5 text-[11px] text-gray-400">
									{entry.kind === 'directory'
										? '文件夹'
										: entry.kind === 'symlink'
											? '符号链接'
											: formatSize(entry.size)}
									<span class="mx-1.5">·</span>
									{new Date(entry.modified_at * 1000).toLocaleString()}
								</div>
							</button>
							{#if entry.kind === 'file'}
								<button
									type="button"
									class="rounded-lg p-2 text-gray-400 opacity-0 transition hover:bg-white hover:text-gray-900 group-hover:opacity-100 dark:hover:bg-gray-700 dark:hover:text-white"
									on:click={() => saveDownload('download', entry.path, entry.name)}
									disabled={Boolean(downloading)}
									aria-label={`下载 ${entry.name}`}
								>
									<svg
										viewBox="0 0 24 24"
										class="size-4"
										fill="none"
										stroke="currentColor"
										stroke-width="1.8"
									>
										<path
											d="M12 3v12m0 0 4-4m-4 4-4-4M5 20h14"
											stroke-linecap="round"
											stroke-linejoin="round"
										/>
									</svg>
								</button>
							{/if}
						</div>
					{/each}
				</div>
			{/if}
		</div>
	</div>
</Modal>
