<script lang="ts">
	import { page } from '$app/state';
	import { api, type CapacityDimension, type CapacityDimensionRow, type CapacityPolicyView } 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 { toastError, toastSuccess } from '$lib/toast';

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

	// Friendlier, user-facing labels. No spec jargon (墙钟 → 运行时长, etc.).
	const DIMENSION_LABELS: Record<CapacityDimension, string> = {
		requestRate: '请求速率',
		requestBodySize: '请求体大小',
		agentConcurrency: '并发数',
		admissionQueueLength: '队列长度',
		admissionQueueWait: '队列等待',
		fileSize: '单文件大小',
		attachmentCount: '附件数',
		archiveExpansion: '归档展开',
		projectStorage: '项目存储',
		organizationStorage: '组织存储',
		memberCount: '成员数',
		projectCount: '项目数',
		teamCount: '团队数',
		folderCount: '文件夹数',
		sessionCount: '会话数',
		runWallTime: '运行时长',
		runTurns: '对话轮次',
		runToolCalls: '工具调用数',
		toolWallTime: '工具执行时长',
		runOutputSize: '输出大小',
		processMemory: '内存',
		processCpu: 'CPU',
		processCount: '进程数',
	};

	// Logical groupings for higher information density.
	const GROUPS: { title: string; dims: CapacityDimension[] }[] = [
		{ title: 'HTTP 接入', dims: ['requestRate', 'requestBodySize'] },
		{ title: '智能体运行', dims: ['agentConcurrency', 'runWallTime', 'runTurns', 'runToolCalls', 'toolWallTime', 'runOutputSize'] },
		{ title: '接纳队列', dims: ['admissionQueueLength', 'admissionQueueWait'] },
		{ title: '附件与存储', dims: ['fileSize', 'attachmentCount', 'archiveExpansion', 'projectStorage', 'organizationStorage'] },
		{ title: '组织配额', dims: ['memberCount', 'projectCount', 'teamCount', 'folderCount', 'sessionCount'] },
		{ title: '进程资源', dims: ['processMemory', 'processCpu', 'processCount'] },
	];

	let view = $state<CapacityPolicyView | null>(null);
	let drafts = $state<Partial<Record<CapacityDimension, string | number>>>({});
	let loading = $state(true);
	let saving = $state(false);
	let error = $state<string | null>(null);

	async function load() {
		loading = true;
		error = null;
		try {
			view = await api.capacityPolicy(slug);
			drafts = {};
			for (const row of view.dimensions) {
				drafts[row.dimension] = row.organizationLimit === null ? '' : String(row.organizationLimit);
			}
		} catch (err) {
			error = err instanceof Error ? err.message : String(err);
		} finally {
			loading = false;
		}
	}

	function draftText(dim: CapacityDimension): string {
		const raw = drafts[dim];
		if (raw === undefined || raw === null) return '';
		return String(raw).trim();
	}

	// Inline validation: surfaced as the user edits, not on submit. Returns null
	// when the draft is empty (means "use platform ceiling") or valid.
	function draftError(row: CapacityDimensionRow): string | null {
		const text = draftText(row.dimension);
		if (text === '') return null;
		const n = Number.parseInt(text, 10);
		if (!Number.isFinite(n) || n < 1) return '需为正整数';
		if (row.platformCeiling !== null && n > row.platformCeiling) return `不得超过 ${row.platformCeiling}`;
		return null;
	}

	// Live effective value: min(platform ceiling, draft). Reflects the draft
	// before save so the user sees the outcome as they type.
	function liveEffective(row: CapacityDimensionRow): number | null {
		if (row.platformCeiling === null) return null;
		const text = draftText(row.dimension);
		if (text === '') return row.platformCeiling;
		const n = Number.parseInt(text, 10);
		if (!Number.isFinite(n) || n < 1) return row.platformCeiling;
		return Math.min(row.platformCeiling, n);
	}

	function isLowered(row: CapacityDimensionRow): boolean {
		const eff = liveEffective(row);
		return eff !== null && eff < row.platformCeiling!;
	}

	const hasErrors = $derived(
		view?.dimensions.some((row) => draftError(row) !== null) ?? false,
	);

	async function save() {
		if (!view || hasErrors) return;
		saving = true;
		const limits: Partial<Record<CapacityDimension, number | null>> = {};
		for (const row of view.dimensions) {
			const text = draftText(row.dimension);
			limits[row.dimension] = text === '' ? null : Number.parseInt(text, 10);
		}
		try {
			view = await api.setCapacityPolicy(slug, { limits });
			drafts = {};
			for (const row of view.dimensions) {
				drafts[row.dimension] = row.organizationLimit === null ? '' : String(row.organizationLimit);
			}
			toastSuccess('容量策略已保存');
		} catch (err) {
			toastError(err instanceof Error ? err.message : String(err));
		} finally {
			saving = false;
		}
	}

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

<PageHeader
	title="容量策略"
	description="平台上限不可突破。组织可在此设更低限制，未设置时按平台上限执行。"
>
	{#snippet actions()}
		<button class="saas-btn-primary" disabled={saving || !view || hasErrors} onclick={save}>
			{saving ? '保存中…' : '保存'}
		</button>
	{/snippet}
</PageHeader>

<p class="saas-muted mb-6">
	未配置平台上限的维度暂不可设置组织限制。输入框留空即沿用平台上限。
</p>

{#if loading}
	<LoadingState />
{:else if error}
	<ErrorBanner message={error} onretry={load} />
{:else if view}
	{#if view.dimensions.length === 0}
		<EmptyState title="暂无容量维度" description="容量维度由平台定义。" />
	{:else}
		{@const policy = view}
		<div class="grid gap-4 md:grid-cols-2">
			{#each GROUPS as group}
				{@const rows = group.dims
					.map((d) => policy.dimensions.find((r) => r.dimension === d))
					.filter((r): r is CapacityDimensionRow => r !== undefined)}
				{#if rows.length > 0}
					<section class="saas-card">
						<header class="group-header">
							<h3 class="group-title">{group.title}</h3>
							<span class="group-count">{rows.length} 项</span>
						</header>
						<div class="dim-list">
							{#each rows as row (row.dimension)}
								{@const err = draftError(row)}
								{@const eff = liveEffective(row)}
								{@const lowered = isLowered(row)}
								{@const disabled = row.platformCeiling === null}
								<div class="dim-row" class:dim-row-error={err !== null}>
									<div class="dim-label">{DIMENSION_LABELS[row.dimension] ?? row.dimension}</div>
									<div class="dim-ceiling">
										{#if disabled}
											<span class="text-surface-400">未配置</span>
										{:else}
											<span class="tabular-nums">平台 ≤ {row.platformCeiling}</span>
										{/if}
									</div>
									<input
										class="saas-input dim-input"
										type="number"
										placeholder="用平台值"
										disabled={disabled}
										bind:value={drafts[row.dimension]}
									/>
									<div class="dim-effective">
										{#if eff === null}
											<span class="text-surface-400">—</span>
										{:else if lowered}
											<span class="saas-badge saas-badge-primary tabular-nums">有效 {eff}</span>
										{:else}
											<span class="saas-badge saas-badge-neutral tabular-nums">有效 {eff}</span>
										{/if}
									</div>
									{#if err !== null}
										<div class="dim-error">{err}</div>
									{/if}
								</div>
							{/each}
						</div>
					</section>
				{/if}
			{/each}
		</div>
	{/if}
{:else}
	<EmptyState title="容量数据不可用" description="无法加载容量策略。" />
{/if}

<style>
	.group-header {
		display: flex;
		align-items: baseline;
		justify-content: space-between;
		padding: 0.5rem 0.75rem;
		border-bottom: 1px solid var(--color-surface-200);
		background: var(--color-surface-100);
	}
	.group-title {
		font-size: 0.8125rem;
		font-weight: 600;
		color: var(--color-surface-800);
		letter-spacing: 0.02em;
	}
	.group-count {
		font-size: 0.6875rem;
		color: var(--color-surface-500);
	}
	.dim-list {
		display: flex;
		flex-direction: column;
	}
	.dim-row {
		display: grid;
		grid-template-columns: minmax(5rem, 1fr) auto minmax(7rem, 8.5rem) 6.5rem;
		align-items: center;
		gap: 0.5rem;
		padding: 0.4rem 0.75rem;
		border-bottom: 1px solid var(--color-surface-100);
	}
	.dim-row:last-child {
		border-bottom: none;
	}
	.dim-row-error {
		background: color-mix(in oklab, var(--color-error-100) 40%, transparent);
	}
	.dim-label {
		font-size: 0.8125rem;
		font-weight: 500;
		color: var(--color-surface-900);
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}
	.dim-ceiling {
		font-size: 0.6875rem;
		color: var(--color-surface-500);
		white-space: nowrap;
	}
	.dim-input {
		padding: 0.25rem 0.5rem;
		font-size: 0.8125rem;
		text-align: right;
	}
	.dim-effective {
		display: flex;
		justify-content: flex-end;
	}
	.dim-effective > .saas-badge {
		width: 100%;
		justify-content: center;
	}
	.dim-error {
		grid-column: 1 / -1;
		font-size: 0.6875rem;
		color: var(--color-error-700);
		padding-bottom: 0.2rem;
	}
	@media (max-width: 480px) {
		.dim-row {
			grid-template-columns: 1fr 5rem;
			grid-template-rows: auto auto auto;
			row-gap: 0.25rem;
		}
		.dim-label {
			grid-column: 1;
		}
		.dim-ceiling {
			grid-column: 2;
			text-align: right;
		}
		.dim-input {
			grid-column: 1 / -1;
		}
		.dim-effective {
			grid-column: 1 / -1;
			justify-content: flex-start;
		}
		.dim-effective > .saas-badge {
			width: auto;
			justify-content: flex-start;
		}
	}
</style>
