<script lang="ts">
	import { page } from '$app/state';
	import { api, type AgentConfigFolderRow, type AgentModelRow, type AgentRoleRow, type AgentSkillRow } from '$lib/api';
	import { session } from '$lib/session';
	import { resolveOrg } from '$lib/org';
	import PageHeader from '$lib/components/PageHeader.svelte';
	import LoadingState from '$lib/components/LoadingState.svelte';
	import ErrorBanner from '$lib/components/ErrorBanner.svelte';
	import EmptyState from '$lib/components/EmptyState.svelte';
	import RoleCard from '$lib/components/RoleCard.svelte';
	import AgentConfigFolderNav from '$lib/components/AgentConfigFolderNav.svelte';
	import Modal from '$lib/components/Modal.svelte';
	import SelectField from '$lib/components/SelectField.svelte';
	import { toastError, toastSuccess } from '$lib/toast';

	const org = $derived(resolveOrg($session.me, page.url.search));
	const slug = $derived(org?.slug ?? '');

	let roles = $state<AgentRoleRow[]>([]);
	let models = $state<AgentModelRow[]>([]);
	let skills = $state<AgentSkillRow[]>([]);
	let folders = $state<AgentConfigFolderRow[]>([]);
	let loading = $state(true);
	let error = $state<string | null>(null);

	/** 'all' | 'unfiled' | folder id (ADR-0028: transparent grouping filter) */
	let selectedFolder = $state<string>('all');

	let newRoleId = $state('');
	let newLabel = $state('');
	let adding = $state(false);

	let showFolderModal = $state(false);
	let folderModalMode = $state<'create' | 'rename'>('create');
	let folderModalId = $state<string | null>(null);
	let folderName = $state('');
	let folderParent = $state('');
	let savingFolder = $state(false);

	async function load() {
		loading = true;
		error = null;
		try {
			const [r, s, f] = await Promise.all([api.agentRoles(slug), api.agentSkills(slug), api.agentConfigFolders(slug)]);
			roles = r.roles;
			skills = s.skills;
			folders = f.folders;
			// Model fetch hits the provider API and may fail or be slow; load it
			// independently so roles remain editable even without a model list.
			models = [];
			api.agentModels(slug)
				.then((m) => { models = m.models; })
				.catch((err) => { toastError(`模型列表加载失败：${err instanceof Error ? err.message : String(err)}`); });
		} catch (err) {
			error = err instanceof Error ? err.message : String(err);
		} finally {
			loading = false;
		}
	}

	const counts = $derived.by(() => {
		const map: Record<string, number> = {};
		for (const r of roles) {
			if (r.folderId) map[r.folderId] = (map[r.folderId] ?? 0) + 1;
		}
		return map;
	});
	const unfiledCount = $derived(roles.filter((r) => !r.folderId).length);
	const visibleRoles = $derived(
		selectedFolder === 'all'
			? roles
			: selectedFolder === 'unfiled'
				? roles.filter((r) => !r.folderId)
				: roles.filter((r) => r.folderId === selectedFolder),
	);

	function folderPath(f: AgentConfigFolderRow): string {
		const parts: string[] = [f.name];
		let cur: AgentConfigFolderRow | undefined = f;
		while (cur?.parentId) {
			const parent = folders.find((x) => x.id === cur!.parentId);
			if (!parent) break;
			parts.unshift(parent.name);
			cur = parent;
		}
		return parts.join(' / ');
	}

	/** Self + descendant ids of a folder — excluded as move targets in the rename modal. */
	function subtreeIds(folderId: string): Set<string> {
		const ids = new Set<string>([folderId]);
		let grew = true;
		while (grew) {
			grew = false;
			for (const f of folders) {
				if (f.parentId && ids.has(f.parentId) && !ids.has(f.id)) {
					ids.add(f.id);
					grew = true;
				}
			}
		}
		return ids;
	}

	const folderItems = $derived([
		{ value: '', label: '（未分类）' },
		...folders.map((f) => ({ value: f.id, label: folderPath(f) })),
	]);

	const moveTargetItems = $derived.by(() => {
		if (folderModalMode !== 'rename' || folderModalId === null) {
			return [{ value: '', label: '（根）' }, ...folders.map((f) => ({ value: f.id, label: folderPath(f) }))];
		}
		const excluded = subtreeIds(folderModalId);
		return [
			{ value: '', label: '（根）' },
			...folders.filter((f) => !excluded.has(f.id)).map((f) => ({ value: f.id, label: folderPath(f) })),
		];
	});

	async function add() {
		const roleId = newRoleId.trim();
		const label = newLabel.trim();
		if (roleId === '' || label === '') {
			toastError('角色 ID 与显示名均为必填');
			return;
		}
		if (roles.some((r) => r.roleId === roleId)) {
			toastError(`角色 ID 已存在：${roleId}`);
			return;
		}
		adding = true;
		try {
			const created = await api.upsertAgentRole(slug, roleId, { label });
			let folderId: string | null = null;
			if (selectedFolder !== 'all' && selectedFolder !== 'unfiled') {
				await api.setAgentRoleFolder(slug, roleId, selectedFolder);
				folderId = selectedFolder;
			}
			roles = [...roles, { ...created, folderId }];
			newRoleId = '';
			newLabel = '';
			toastSuccess('角色已创建');
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			adding = false;
		}
	}

	function onRoleUpdated(updated: AgentRoleRow) {
		roles = roles.map((x) => (x.roleId === updated.roleId ? { ...updated, skillNames: x.skillNames } : x));
		if (updated.isDefault) {
			roles = roles.map((x) => (x.roleId === updated.roleId ? x : { ...x, isDefault: false }));
		}
	}

	function onRoleSkillsChanged(roleId: string, skillNames: string[]) {
		roles = roles.map((x) => (x.roleId === roleId ? { ...x, skillNames } : x));
	}

	function onRoleFolderChanged(roleId: string, folderId: string | null) {
		roles = roles.map((x) => (x.roleId === roleId ? { ...x, folderId } : x));
	}

	function openCreateFolder(parentId: string | null) {
		folderModalMode = 'create';
		folderModalId = null;
		folderName = '';
		folderParent = parentId ?? '';
		showFolderModal = true;
	}

	function openRenameFolder(folder: AgentConfigFolderRow) {
		folderModalMode = 'rename';
		folderModalId = folder.id;
		folderName = folder.name;
		folderParent = folder.parentId ?? '';
		showFolderModal = true;
	}

	async function submitFolderModal() {
		const name = folderName.trim();
		if (name === '') {
			toastError('文件夹名称不能为空');
			return;
		}
		savingFolder = true;
		try {
			if (folderModalMode === 'create') {
				await api.createAgentConfigFolder(slug, {
					name,
					...(folderParent !== '' ? { parentId: folderParent } : {}),
				});
				toastSuccess('文件夹已创建');
			} else if (folderModalId !== null) {
				await api.patchAgentConfigFolder(slug, folderModalId, {
					name,
					parentId: folderParent === '' ? null : folderParent,
				});
				toastSuccess('文件夹已更新');
			}
			showFolderModal = false;
			await load();
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			savingFolder = false;
		}
	}

	async function deleteFolder(folder: AgentConfigFolderRow) {
		if (!confirm(`删除文件夹「${folder.name}」? 仅空文件夹可删除。`)) return;
		try {
			await api.deleteAgentConfigFolder(slug, folder.id);
			if (selectedFolder === folder.id) selectedFolder = 'all';
			toastSuccess('文件夹已删除');
			await load();
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		}
	}

	$effect(() => {
		if (slug) load();
	});
</script>

<PageHeader
	title="角色"
	description="角色是组织级数据：组合默认模型、系统提示词、工具白名单与已绑定技能。文件夹仅作管理分组，不影响角色解析与默认角色约束。"
/>

{#if loading}
	<LoadingState />
{:else if error}
	<ErrorBanner message={error} onretry={load} />
{:else}
	<div class="grid gap-4 lg:grid-cols-[15rem_1fr]">
		<div class="h-fit lg:sticky lg:top-4">
			<AgentConfigFolderNav
				{folders}
				selected={selectedFolder}
				{counts}
				totalCount={roles.length}
				{unfiledCount}
				onselect={(id) => (selectedFolder = id)}
				oncreate={openCreateFolder}
				onrename={openRenameFolder}
				ondelete={deleteFolder}
			/>
		</div>

		<div>
			<div class="saas-card-pad mb-6">
				<h2 class="saas-section-title mb-4">新建角色</h2>
				<div class="grid gap-3 sm:grid-cols-[10rem_1fr_auto]">
					<input
						class="saas-input font-mono text-sm"
						placeholder="角色 ID（如 draft）"
						bind:value={newRoleId}
						onkeydown={(e) => {
							if (e.key === 'Enter') add();
						}}
					/>
					<input
						class="saas-input"
						placeholder="显示名（如 草稿）"
						bind:value={newLabel}
						onkeydown={(e) => {
							if (e.key === 'Enter') add();
						}}
					/>
					<button class="saas-btn-primary" onclick={add} disabled={adding}>新建</button>
				</div>
				<p class="mt-2 text-xs text-surface-600">角色 ID 仅允许小写字母、数字、下划线与连字符，且以字母或数字开头；当前选中文件夹时新角色会自动归入其中。</p>
			</div>

			{#if roles.length === 0}
				<div class="saas-card">
					<EmptyState title="暂无角色" description="组织必须且只能有一个启用中的默认角色；新建第一个角色将自动成为默认。" />
				</div>
			{:else if visibleRoles.length === 0}
				<div class="saas-card">
					<EmptyState title="此分类下暂无角色" description="在角色卡片上可将其移入当前文件夹。" />
				</div>
			{:else}
				<div class="space-y-4">
					{#each visibleRoles as r (r.roleId)}
						<RoleCard
							{r}
							{models}
							{skills}
							{slug}
							{folderItems}
							onupdated={onRoleUpdated}
							onskillschanged={onRoleSkillsChanged}
							onfolderchanged={onRoleFolderChanged}
						/>
					{/each}
				</div>
			{/if}
		</div>
	</div>

	<Modal bind:open={showFolderModal} title={folderModalMode === 'create' ? '新建文件夹' : '重命名 / 移动文件夹'}>
		<label class="saas-label" for="agent-folder-name">名称</label>
		<input
			id="agent-folder-name"
			class="saas-input mb-4"
			bind:value={folderName}
			onkeydown={(e) => {
				if (e.key === 'Enter') submitFolderModal();
			}}
		/>
		<p class="saas-label">父文件夹</p>
		<div class="mb-4">
			<SelectField items={moveTargetItems} bind:value={folderParent} />
		</div>
		<div class="flex justify-end gap-2">
			<button class="saas-btn-ghost" onclick={() => (showFolderModal = false)}>取消</button>
			<button class="saas-btn-primary" onclick={submitFolderModal} disabled={savingFolder}>
				{savingFolder ? '保存中…' : '保存'}
			</button>
		</div>
	</Modal>
{/if}
