<script lang="ts">
	import type { AgentSkillRow, SkillFileEntry } from '$lib/api';
	import { api } from '$lib/api';
	import { fmtDate } from '$lib/format';
	import Icon from '$lib/components/Icon.svelte';
	import SelectField from '$lib/components/SelectField.svelte';
	import { toastError, toastSuccess } from '$lib/toast';

	let {
		slug,
		skill,
		folderItems,
		oninstalled,
		ondisabled,
		onfolderchanged,
	}: {
		slug: string;
		skill: AgentSkillRow;
		/** ADR-0028 folder choices ('' = 未分类); transparent grouping only */
		folderItems: { value: string; label: string }[];
		oninstalled: (result: { id: string; name: string; contentDigest: string }) => void;
		ondisabled: (name: string) => void;
		onfolderchanged: (name: string, folderId: string | null) => void;
	} = $props();

	type FileNode = { path: string; content: string };

	let files = $state<FileNode[]>([]);
	let selectedPath = $state<string | null>(null);
	let version = $state(skill.version);
	let description = $state(skill.description ?? '');
	let loading = $state(false);
	let saving = $state(false);
	let dirty = $state(false);
	let newFilePath = $state('');
	let showNewFile = $state(false);
	let folderValue = $state(skill.folderId ?? '');
	let savingFolder = $state(false);

	const selectedFile = $derived(files.find((f) => f.path === selectedPath) ?? null);
	const hasManifest = $derived(files.some((f) => f.path === 'SKILL.md'));
	const sortedFiles = $derived([...files].sort((a, b) => a.path.localeCompare(b.path)));

	async function loadFiles() {
		loading = true;
		try {
			const res = await api.agentSkillFiles(slug, skill.name);
			files = res.files.map((f) => ({ path: f.path, content: f.content }));
			if (files.length > 0 && selectedPath === null) {
				selectedPath = files[0]!.path;
			}
			dirty = false;
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			loading = false;
		}
	}

	function selectFile(path: string) {
		selectedPath = path;
	}

	function updateContent(path: string, content: string) {
		const file = files.find((f) => f.path === path);
		if (file) {
			file.content = content;
			dirty = true;
			if (path === 'SKILL.md') {
				const desc = parseDescription(content);
				if (desc !== null) description = desc;
			}
		}
	}

	function addFile() {
		const path = newFilePath.trim();
		if (path === '') {
			toastError('文件路径不能为空');
			return;
		}
		if (files.some((f) => f.path === path)) {
			toastError(`文件已存在：${path}`);
			return;
		}
		if (path.startsWith('/') || path.includes('..')) {
			toastError('文件路径必须为相对路径');
			return;
		}
		files.push({ path, content: '' });
		selectedPath = path;
		newFilePath = '';
		showNewFile = false;
		dirty = true;
	}

	function deleteFile(path: string) {
		if (path === 'SKILL.md') {
			toastError('SKILL.md 是必需的 manifest，不能删除');
			return;
		}
		files = files.filter((f) => f.path !== path);
		if (selectedPath === path) {
			selectedPath = files.length > 0 ? files[0]!.path : null;
		}
		dirty = true;
	}

	function parseDescription(manifest: string): string | null {
		const match = /^description:\s*['"]?([^'"\r\n]+)['"]?\s*$/m.exec(manifest);
		return match ? match[1]!.trim() : null;
	}

	async function save() {
		if (!hasManifest) {
			toastError('缺少 SKILL.md manifest 文件');
			return;
		}
		const trimmedVersion = version.trim();
		if (trimmedVersion === '') {
			toastError('版本号不能为空');
			return;
		}
		saving = true;
		try {
			const fileEntries: SkillFileEntry[] = files.map((f) => ({ path: f.path, content: f.content }));
			const result = await api.installAgentSkill(slug, skill.name, {
				version: trimmedVersion,
				files: fileEntries,
			});
			dirty = false;
			toastSuccess('技能已保存');
			oninstalled(result);
			await loadFiles();
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			saving = false;
		}
	}

	async function disable() {
		saving = true;
		try {
			await api.patchAgentSkill(slug, skill.name, { disabled: true });
			toastSuccess('技能已禁用');
			ondisabled(skill.name);
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			saving = false;
		}
	}

	function saveDescription() {
		const manifest = files.find((f) => f.path === 'SKILL.md');
		if (!manifest) return;
		const updated = updateFrontmatter(manifest.content, 'description', description.trim());
		manifest.content = updated;
		dirty = true;
	}

	// ADR-0028: folder assignment is a label-class change — instant-apply, no
	// session archival, independent of the content save button.
	async function saveFolder(next: string) {
		const folderId = next === '' ? null : next;
		if (folderId === skill.folderId) return;
		savingFolder = true;
		try {
			await api.setAgentSkillFolder(slug, skill.name, folderId);
			onfolderchanged(skill.name, folderId);
			toastSuccess('已更新所属文件夹');
		} catch (err) {
			folderValue = skill.folderId ?? '';
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			savingFolder = false;
		}
	}

	function updateFrontmatter(content: string, key: string, value: string): string {
		const regex = new RegExp(`^(${key}:\\s*)(.*?)(\\s*)$`, 'm');
		if (regex.test(content)) {
			return content.replace(regex, `${key}: ${value}`);
		}
		const lines = content.split('\n');
		if (lines[0] === '---') {
			lines.splice(1, 0, `${key}: ${value}`);
		} else {
			lines.unshift('---', `${key}: ${value}`, '---');
		}
		return lines.join('\n');
	}

	$effect(() => {
		if (skill.name) loadFiles();
	});
</script>

<div class="saas-card-pad">
	<div class="mb-4 flex flex-wrap items-center gap-2">
		<span class="saas-badge-primary font-mono">{skill.name}</span>
		<span class="text-sm text-surface-700">v{skill.version}</span>
		{#if skill.disabledAt}
			<span class="saas-badge-error">已禁用</span>
		{/if}
		<div class="ml-auto flex items-center gap-2">
			<span class="text-xs text-surface-600">文件夹</span>
			<div class="w-44">
				<SelectField items={folderItems} bind:value={folderValue} disabled={savingFolder} onchange={saveFolder} />
			</div>
		</div>
	</div>

	<div class="mb-4 grid grid-cols-1 gap-4 md:grid-cols-2">
		<div>
			<label for="skill-version-{skill.id}" class="saas-label">版本号</label>
			<input id="skill-version-{skill.id}" class="saas-input" bind:value={version} placeholder="如 0.1.0" />
		</div>
		<div>
			<label for="skill-desc-{skill.id}" class="saas-label">描述（同步到 SKILL.md frontmatter）</label>
			<div class="flex gap-2">
				<input id="skill-desc-{skill.id}" class="saas-input" bind:value={description} onchange={saveDescription} />
			</div>
		</div>
	</div>

	<div class="mb-4 flex flex-wrap items-center gap-2 text-xs text-surface-600">
		<span>digest: <code class="font-mono">{skill.contentDigest.slice(0, 12)}</code></span>
		<span>·</span>
		<span>更新于 {fmtDate(skill.updatedAt)}</span>
		{#if skill.boundRoleIds.length > 0}
			<span>·</span>
			<span>绑定角色: {skill.boundRoleIds.join(', ')}</span>
		{/if}
		{#if dirty}
			<span>·</span>
			<span class="font-medium text-warning-700">未保存</span>
		{/if}
	</div>

	{#if loading}
		<div class="py-8 text-center text-sm text-surface-600">加载文件中…</div>
	{:else}
		<div class="grid grid-cols-1 gap-4 md:grid-cols-[16rem_1fr]">
			<div class="border border-surface-300 bg-surface-100">
				<div class="flex items-center justify-between border-b border-surface-300 px-3 py-2">
					<span class="text-xs font-medium text-surface-700">文件</span>
					<button
						type="button"
						class="text-xs text-primary-700 hover:text-primary-900"
						onclick={() => (showNewFile = !showNewFile)}
					>
						{showNewFile ? '取消' : '+ 新增'}
					</button>
				</div>
				{#if showNewFile}
					<div class="border-b border-surface-300 px-3 py-2">
						<input
							class="saas-input text-xs"
							placeholder="如 reference.md"
							bind:value={newFilePath}
							onkeydown={(e) => {
								if (e.key === 'Enter') addFile();
							}}
						/>
					</div>
				{/if}
				<div class="max-h-80 overflow-y-auto">
					{#each sortedFiles as f (f.path)}
						<button
							type="button"
							class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm transition hover:bg-surface-200
								{selectedPath === f.path ? 'bg-primary-100 text-primary-900' : 'text-surface-800'}"
							onclick={() => selectFile(f.path)}
						>
							<Icon name="file" class="h-3.5 w-3.5 shrink-0 opacity-60" />
							<span class="truncate font-mono text-xs">{f.path}</span>
							{#if f.path === 'SKILL.md'}
								<span class="ml-auto text-[10px] text-primary-700">manifest</span>
							{:else}
								<span
									class="ml-auto text-xs text-surface-500 hover:text-error-700"
									role="button"
									tabindex="0"
									onclick={(e) => {
										e.stopPropagation();
										deleteFile(f.path);
									}}
									onkeydown={(e) => {
										if (e.key === 'Enter' || e.key === ' ') {
											e.stopPropagation();
											deleteFile(f.path);
										}
									}}
								>
									×
								</span>
							{/if}
						</button>
					{/each}
					{#if files.length === 0}
						<div class="px-3 py-4 text-center text-xs text-surface-600">暂无文件</div>
					{/if}
				</div>
			</div>

			<div>
				{#if selectedFile}
					<div class="mb-2 flex items-center gap-2">
						<span class="font-mono text-xs text-surface-600">{selectedFile.path}</span>
					</div>
					<textarea
						class="h-80 w-full resize-y border border-surface-300 bg-white p-3 font-mono text-xs text-surface-900 focus:border-primary-500 focus:outline-none"
						bind:value={selectedFile.content}
						oninput={() => (dirty = true)}
					></textarea>
				{:else}
					<div class="flex h-80 items-center justify-center border border-surface-300 bg-surface-100 text-sm text-surface-600">
						选择一个文件或新建文件
					</div>
				{/if}
			</div>
		</div>

		<div class="mt-4 flex flex-wrap items-center gap-3 border-t border-surface-100 pt-4">
			<button class="saas-btn-primary" onclick={save} disabled={saving || !dirty}>
				{saving ? '保存中…' : '保存'}
			</button>
			{#if !skill.disabledAt}
				<button class="saas-btn-danger" onclick={disable} disabled={saving}>
					禁用
				</button>
			{/if}
		</div>
	{/if}
</div>
