import type {
	SubagentEntryProjection,
	SubagentProjectionDiagnostics,
	SubagentEvidenceSummary,
	SubagentProcessEvent,
	SubagentTaskPanel,
	SubagentToolExecution,
	SessionTreeEntry,
	SessionTreeSubagentResultMetadata,
	SessionTreeSubagentResultContent,
} from '../types/index.d';

interface NormalizedSubagentResultMetadata {
	evidenceSummaries: SubagentEvidenceSummary[];
	processEvents: SubagentProcessEvent[];
	finalResults: Record<string, unknown>[];
}

export function extractSubagentEvidenceSummariesByEntry(entries: SessionTreeEntry[]): Record<string, SubagentEvidenceSummary[]> {
	const next: Record<string, SubagentEvidenceSummary[]> = {};

	for (const entry of entries) {
		const projection = getCanonicalSubagentProjection(entry);
		if (projection) {
			if (projection.evidenceSummaries.length > 0) {
				next[entry.id] = projection.evidenceSummaries.slice(0, 8);
			}
			continue;
		}

		const results = getLegacySubagentResultRecords(entry);
		const metadata = getNormalizedSubagentResultMetadata(entry);
		const resultSummaries = results
			.map(item => (item.result as any)?.resultPayload?.evidenceSummary)
			.filter((item): item is SubagentEvidenceSummary => isEvidenceSummary(item));
		const summaries = [...metadata.evidenceSummaries, ...resultSummaries].slice(0, 8);
		if (summaries.length > 0) {
			next[entry.id] = summaries;
		}
	}

	return next;
}

export function extractSubagentProcessEventsByEntry(entries: SessionTreeEntry[]): Record<string, SubagentProcessEvent[]> {
	const next: Record<string, SubagentProcessEvent[]> = {};

	for (const entry of entries) {
		const projection = getCanonicalSubagentProjection(entry);
		if (projection) {
			if (projection.processEvents.length > 0) {
				next[entry.id] = projection.processEvents.slice(0, 50);
			}
			continue;
		}

		const results = getLegacySubagentResultRecords(entry);
		const metadata = getNormalizedSubagentResultMetadata(entry);
		const resultEvents = results
			.flatMap(item => toProcessEventArray((item.result as any)?.resultPayload?.processEvents));
		const events = [...metadata.processEvents, ...resultEvents].slice(0, 50);
		if (events.length > 0) {
			next[entry.id] = events;
		}
	}

	return next;
}

export function extractSubagentTaskPanelsByEntry(entries: SessionTreeEntry[]): Record<string, SubagentTaskPanel[]> {
	const next: Record<string, SubagentTaskPanel[]> = {};

	for (const entry of entries) {
		const projection = getCanonicalSubagentProjection(entry);
		if (projection) {
			if (projection.taskPanels.length > 0) {
				next[entry.id] = projection.taskPanels
					.slice()
					.sort((a, b) => (a.sortOrder ?? Number.MAX_SAFE_INTEGER) - (b.sortOrder ?? Number.MAX_SAFE_INTEGER));
			}
			continue;
		}

		const results = getLegacySubagentResultRecords(entry);
		if (results.length === 0) continue;
		const panels = results
			.map((item, index) => toSubagentTaskPanel(item.result, index, item.task))
			.filter((item): item is SubagentTaskPanel => Boolean(item))
			.sort((a, b) => (a.sortOrder ?? Number.MAX_SAFE_INTEGER) - (b.sortOrder ?? Number.MAX_SAFE_INTEGER));
		if (panels.length > 0) {
			next[entry.id] = panels;
		}
	}

	return next;
}

export function extractSubagentProjectionDiagnosticsByEntry(entries: SessionTreeEntry[]): Record<string, SubagentProjectionDiagnostics> {
	const next: Record<string, SubagentProjectionDiagnostics> = {};

	for (const entry of entries) {
		const projection = getCanonicalSubagentProjection(entry);
		if (projection?.diagnostics) {
			next[entry.id] = {
				...projection.diagnostics,
				projectionPath: projection.diagnostics.projectionPath || 'canonical-backend-projection',
				inputSources: [...projection.diagnostics.inputSources],
				ignoredSources: [...projection.diagnostics.ignoredSources],
			};
			continue;
		}

		const synthetic = buildSyntheticProjectionDiagnostics(entry);
		if (!synthetic) continue;
		next[entry.id] = synthetic;
	}

	return next;
}

export function buildSubagentToolExecutions(events: SubagentProcessEvent[]): SubagentToolExecution[] {
	return buildSubagentToolExecutionProjection(events).executions;
}

export function extractTreeNodeContextMeta(entry: SessionTreeEntry): string[] {
	if (entry.type === 'branch_summary') {
		const fromId = shortEntryRef(
			typeof entry.fromId === 'string' && entry.fromId.trim()
				? entry.fromId
				: typeof entry.targetId === 'string' && entry.targetId.trim()
					? entry.targetId
					: ''
		);
		return fromId ? [`来源 ${fromId}`] : [];
	}

	if (entry.type === 'compaction') {
		const parts: string[] = [];
		const before = entry.tokensBefore || 0;
		const after = compactionTokensAfter(entry);
		parts.push(typeof after === 'number' ? `${before} -> ${after}` : `${before} -> 未知`);

		const boundary = shortEntryRef(entry.firstKeptEntryId);
		if (boundary) {
			parts.push(`边界 ${boundary}`);
		}
		return parts;
	}

	return [];
}

export function describeComposerTarget(
	entry: SessionTreeEntry | null,
	leafEntryId: string | null | undefined,
	entryById?: Record<string, SessionTreeEntry | undefined>
) {
	const leafEntry = leafEntryId && entryById ? entryById[leafEntryId] || null : null;
	const leafTitle = leafEntry ? summarizeEntryTitle(leafEntry) : '当前叶子';

	if (!entry) {
		return {
			mode: 'leaf' as const,
			headline: '发送到当前叶子',
			hint: '当前没有单独选中的节点，新消息会直接追加到当前会话叶子。',
			branchBaseTitle: '',
			replayUserMessage: '',
			currentLeafTitle: leafTitle,
			switchTargetTitle: '',
			deliveryTargetTitle: leafTitle,
		};
	}

	if (entry.type === 'message' && entry.message?.role === 'user') {
		const parentEntry = entry.parentId && entryById ? entryById[entry.parentId] || null : null;
		const parentTitle = parentEntry ? summarizeEntryTitle(parentEntry) : '根节点';
		const replayUserMessage = summarizeStringContent(entry.message.content);
		return {
			mode: 'branch' as const,
			headline: `基于用户节点分支`,
			hint: '发送后会以该用户节点的父节点为基线，新建一条分支，并回填这条用户消息。',
			branchBaseTitle: parentTitle,
			replayUserMessage,
			currentLeafTitle: leafTitle,
			switchTargetTitle: parentTitle,
			deliveryTargetTitle: replayUserMessage || summarizeEntryTitle(entry),
		};
	}

	if (entry.id === leafEntryId) {
		const entryTitle = summarizeEntryTitle(entry);
		return {
			mode: 'leaf' as const,
			headline: '发送到当前叶子',
			hint: '当前选中的就是会话叶子，新消息会直接接在这里。',
			branchBaseTitle: '',
			replayUserMessage: '',
			currentLeafTitle: entryTitle,
			switchTargetTitle: '',
			deliveryTargetTitle: entryTitle,
		};
	}

	if (entry.type === 'branch_summary') {
		const entryTitle = summarizeEntryTitle(entry);
		return {
			mode: 'switch' as const,
			headline: '从分支摘要继续',
			hint: '发送前会先切到这个分支摘要节点，然后沿摘要后的路径继续。',
			branchBaseTitle: '',
			replayUserMessage: '',
			currentLeafTitle: leafTitle,
			switchTargetTitle: entryTitle,
			deliveryTargetTitle: entryTitle,
		};
	}

	if (entry.type === 'compaction') {
		const entryTitle = summarizeEntryTitle(entry);
		return {
			mode: 'switch' as const,
			headline: '从压缩节点继续',
			hint: '发送前会先切到这个压缩节点，然后基于压缩摘要和保留边界之后的上下文继续。',
			branchBaseTitle: '',
			replayUserMessage: '',
			currentLeafTitle: leafTitle,
			switchTargetTitle: entryTitle,
			deliveryTargetTitle: entryTitle,
		};
	}

	const entryTitle = summarizeEntryTitle(entry);
	return {
		mode: 'switch' as const,
		headline: '切换后继续',
		hint: '发送前会先切到当前选中节点，再沿该节点所在路径继续。',
		branchBaseTitle: '',
		replayUserMessage: '',
		currentLeafTitle: leafTitle,
		switchTargetTitle: entryTitle,
		deliveryTargetTitle: entryTitle,
	};
}

export function describeSelectionState(
	selectedEntryId: string | null | undefined,
	leafEntryId: string | null | undefined,
	switchingLeafEntryId: string | null | undefined,
	pendingLeafConfirmation?:
		| {
			entryId: string;
			previousLeafEntryId: string | null;
		}
		| null
) {
	if (switchingLeafEntryId) {
		return {
			kind: 'switching' as const,
			label: '切换中',
			hint: '正在把当前会话叶子切换到这个节点。',
		};
	}
	if (pendingLeafConfirmation?.entryId && selectedEntryId === pendingLeafConfirmation.entryId) {
		return {
			kind: 'pending' as const,
			label: '待确认',
			hint: '已切到本地 fallback 路径，但服务端尚未确认叶子切换；发送前请留意续写目标。',
		};
	}
	if (selectedEntryId && leafEntryId && selectedEntryId === leafEntryId) {
		return {
			kind: 'leaf' as const,
			label: '当前叶子',
			hint: '这个节点已经是当前会话叶子，新消息会直接接在这里。',
		};
	}
	if (selectedEntryId) {
		return {
			kind: 'selected' as const,
			label: '仅选中',
			hint: '当前只是选中了这个节点，只有执行继续后才会真正切换叶子。',
		};
	}
	return {
		kind: 'leaf' as const,
		label: '跟随当前叶子',
		hint: '当前没有单独选中的节点，界面会跟随会话叶子。',
	};
}

export function describeTreeNodeBadges(
	entry: SessionTreeEntry,
	state: {
		selectedEntryId: string | null | undefined;
		leafEntryId: string | null | undefined;
		activePathIds: string[];
		pendingLeafConfirmation?:
			| {
				entryId: string;
				previousLeafEntryId: string | null;
			}
			| null;
	}
): Array<{ label: string; tone: 'leaf' | 'path' | 'branch' | 'selected' | 'pending' }> {
	const badges: Array<{ label: string; tone: 'leaf' | 'path' | 'branch' | 'selected' | 'pending' }> = [];
	const isLeaf = state.leafEntryId === entry.id;
	const isSelected = state.selectedEntryId === entry.id;
	const isPending = state.pendingLeafConfirmation?.entryId === entry.id;
	const isActivePath = state.activePathIds.includes(entry.id);

	if (isLeaf) {
		badges.push({ label: '叶子', tone: 'leaf' });
	}
	if (isActivePath && !isLeaf) {
		badges.push({ label: '路径中', tone: 'path' });
	}
	if (isPending && !isLeaf) {
		badges.push({ label: '待确认', tone: 'pending' });
	}
	if (entry.type === 'message' && entry.message?.role === 'user' && !isLeaf) {
		badges.push({ label: '可分支', tone: 'branch' });
	}
	if (isSelected && !isLeaf && !isPending) {
		badges.push({ label: '已选中', tone: 'selected' });
	}

	return badges;
}

export function describeContinueFeedback(result: { success: boolean; fallback?: boolean; title?: string; error?: string | null }) {
	if (result.success && result.fallback) {
		return {
			type: 'warning' as const,
			message: `已选中 ${result.title || '目标节点'}，但服务端未确认叶子切换，请留意后续发送是否符合预期。`,
		};
	}
	if (result.success) {
		return {
			type: 'success' as const,
			message: `已切换到 ${result.title || '目标节点'}。`,
		};
	}
	return {
		type: 'error' as const,
		message: result.error || `切换到 ${result.title || '目标节点'} 失败。`,
	};
}

export function describePendingSendGuard(title?: string) {
	return {
		title: '续写目标待确认',
		message: `${title || '当前目标节点'} 仍处于本地 fallback 路径，服务端尚未确认叶子切换。继续发送可能落到非预期路径，请确认是否仍要发送。`,
	};
}

export function summarizeBranchEntry(entry: SessionTreeEntry) {
	const summary = entry.summary?.trim() || stringifyContent(entry.content) || '分支摘要';
	const sourceLabel = extractTreeNodeContextMeta(entry).find(item => item.startsWith('来源 '))?.slice(3) || '未知节点';
	return {
		summary,
		sourceLabel,
		title: summary.length > 72 ? `${summary.slice(0, 69)}...` : summary,
	};
}

export function summarizeCompactionEntry(entry: SessionTreeEntry) {
	const summary = entry.summary?.trim() || stringifyContent(entry.content) || '上下文压缩节点';
	const tokenLabel = extractTreeNodeContextMeta(entry).find(item => item.includes('->')) || `${entry.tokensBefore || 0} -> 未知`;
	const boundaryLabel = extractTreeNodeContextMeta(entry).find(item => item.startsWith('边界 '))?.slice(3) || '';
	return {
		summary,
		tokenLabel,
		boundaryLabel,
		title: `压缩 ${tokenLabel}`,
	};
}

export function describeEntryDisplay(entry: SessionTreeEntry) {
	if (entry.type === 'branch_summary') {
		return summarizeBranchEntry(entry).title;
	}
	if (entry.type === 'compaction') {
		return summarizeCompactionEntry(entry).title;
	}
	if (entry.type === 'message') {
		const raw = stringifyContent(entry.message?.content);
		return stripThinkTags(raw).trim() || `${entry.message?.role || 'message'} 节点`;
	}
	if (entry.summary) return entry.summary;
	if (entry.label) return entry.label;
	return stringifyContent(entry.content || entry.data || entry.type);
}

function stripThinkTags(text: string): string {
	return text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
}

export function describeSubagentProjectionDiagnostics(diagnostics: SubagentProjectionDiagnostics | null | undefined) {
	if (!diagnostics) {
		return {
			summary: '',
			selectedSourceLabel: '',
			inputSourcesLabel: '',
			ignoredSourcesLabel: '',
			projectionPathLabel: '',
		};
	}
	return {
		summary: `${describeSubagentProjectionSource(diagnostics.selectedSource)} / ${diagnostics.projectedTaskCount} 个任务`,
		selectedSourceLabel: describeSubagentProjectionSource(diagnostics.selectedSource),
		inputSourcesLabel: diagnostics.inputSources.map(source => describeSubagentProjectionSource(source)).join(' / '),
		ignoredSourcesLabel: diagnostics.ignoredSources.map(source => describeSubagentProjectionSource(source)).join(' / '),
		projectionPathLabel: describeSubagentProjectionPath(diagnostics.projectionPath),
	};
}

export function getNormalizedSubagentResultMetadata(entry: SessionTreeEntry): NormalizedSubagentResultMetadata {
	const metadata = (entry.metadata || {}) as SessionTreeSubagentResultMetadata;
	return {
		evidenceSummaries: toEvidenceSummaryArray(metadata.evidenceSummaries),
		processEvents: toProcessEventArray(metadata.processEvents),
		finalResults: toRecordArray(metadata.finalResults),
	};
}

interface SubagentResultRecord {
	task?: Record<string, unknown>;
	result: Record<string, unknown>;
}

function shortEntryRef(value: unknown) {
	return typeof value === 'string' && value.trim() ? value.slice(0, 8) : '';
}

function summarizeStringContent(value: unknown) {
	if (typeof value !== 'string') return '';
	const trimmed = value.trim();
	if (!trimmed) return '';
	return trimmed.length > 48 ? `${trimmed.slice(0, 45)}...` : trimmed;
}

export function summarizeEntryTitle(entry: SessionTreeEntry) {
	if (entry.type === 'message') {
		const content = summarizeStringContent(entry.message?.content);
		if (content) return content;
		return `${entry.message?.role || 'message'} 节点`;
	}
	if (entry.summary?.trim()) {
		return entry.summary.trim().slice(0, 48);
	}
	return entry.type;
}

function compactionTokensAfter(entry: SessionTreeEntry) {
	const metadataTokensAfter = entry.metadata && typeof entry.metadata === 'object'
		? (entry.metadata as Record<string, unknown>).tokensAfter
		: undefined;
	const dataTokensAfter = entry.data && typeof entry.data === 'object'
		? (entry.data as Record<string, unknown>).tokensAfter
		: undefined;
	if (typeof metadataTokensAfter === 'number') return metadataTokensAfter;
	if (typeof dataTokensAfter === 'number') return dataTokensAfter;
	return undefined;
}

function describeSubagentProjectionSource(source: SubagentProjectionDiagnostics['selectedSource']) {
	switch (source) {
		case 'subagent_result':
			return '结果节点';
		case 'tool_message':
			return '工具消息';
		case 'message_metadata':
			return '消息元数据';
		case 'entry_metadata':
			return '条目元数据';
		default:
			return source;
	}
}

function describeSubagentProjectionPath(path: SubagentProjectionDiagnostics['projectionPath']) {
	switch (path) {
		case 'canonical-backend-projection':
			return '后端规范投影';
		case 'metadata-only-fallback':
			return '仅 metadata fallback';
		case 'legacy-payload-fallback':
			return '旧委派载荷 fallback';
		default:
			return '';
	}
}

function stringifyContent(content: unknown) {
	if (typeof content === 'string') return content;
	if (content == null) return '';
	try {
		return JSON.stringify(content, null, 2);
	} catch {
		return String(content);
	}
}

function getCanonicalSubagentProjection(entry: SessionTreeEntry): SubagentEntryProjection | null {
	const metadata = entry.metadata;
	if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
		return null;
	}
	const projection = (metadata as Record<string, unknown>).subagentProjection;
	if (!projection || typeof projection !== 'object' || Array.isArray(projection)) {
		return null;
	}
	const record = projection as Partial<SubagentEntryProjection>;
	if (record.version !== 1 || record.source !== 'backend-projection') {
		return null;
	}
	if (!isSubagentReplayProjection(record.replay)) {
		return null;
	}
	if (!Array.isArray(record.taskPanels)) {
		return null;
	}
	return {
		version: 1,
		source: 'backend-projection',
		replay: record.replay,
		evidenceSummaries: toEvidenceSummaryArray(record.evidenceSummaries),
		processEvents: toProcessEventArray(record.processEvents),
		taskPanels: record.taskPanels
			.filter((item): item is SubagentTaskPanel => item && typeof item === 'object' && !Array.isArray(item))
			.map(panel => ({
				...panel,
				evidenceSummaries: toEvidenceSummaryArray(panel.evidenceSummaries),
				processEvents: toProcessEventArray(panel.processEvents),
				toolExecutions: Array.isArray(panel.toolExecutions)
					? panel.toolExecutions.filter(item => item && typeof item === 'object' && !Array.isArray(item))
					: [],
			})),
		diagnostics: isSubagentProjectionDiagnostics(record.diagnostics)
			? {
				...record.diagnostics,
				inputSources: [...record.diagnostics.inputSources],
				ignoredSources: [...record.diagnostics.ignoredSources],
			}
			: {
				selectedSource: 'subagent_result',
				inputSources: ['subagent_result'],
				ignoredSources: [],
				rawResultCount: 0,
				dedupedResultCount: 0,
				projectedTaskCount: 0,
				skippedResultCount: 0,
				fallbackUsed: false,
				projectionPath: 'canonical-backend-projection',
			},
	};
}

function isSubagentReplayProjection(value: unknown): value is SubagentEntryProjection['replay'] {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return false;
	}
	const replay = value as Partial<SubagentEntryProjection['replay']>;
	return isNumericLimitRecord(replay.limits) && isBooleanTruncationRecord(replay.truncated);
}

function isNumericLimitRecord(value: unknown): value is SubagentEntryProjection['replay']['limits'] {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return false;
	}
	const limits = value as Partial<SubagentEntryProjection['replay']['limits']>;
	return typeof limits.evidenceSummaries === 'number'
		&& typeof limits.processEvents === 'number'
		&& typeof limits.taskProcessEvents === 'number'
		&& typeof limits.toolExecutions === 'number';
}

function isBooleanTruncationRecord(value: unknown): value is SubagentEntryProjection['replay']['truncated'] {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return false;
	}
	const truncated = value as Partial<SubagentEntryProjection['replay']['truncated']>;
	return typeof truncated.evidenceSummaries === 'boolean'
		&& typeof truncated.processEvents === 'boolean'
		&& typeof truncated.taskPanels === 'boolean'
		&& typeof truncated.toolExecutions === 'boolean';
}

function isSubagentProjectionDiagnostics(value: unknown): value is SubagentProjectionDiagnostics {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return false;
	}
	const diagnostics = value as Partial<SubagentProjectionDiagnostics>;
	return typeof diagnostics.selectedSource === 'string'
		&& Array.isArray(diagnostics.inputSources)
		&& Array.isArray(diagnostics.ignoredSources)
		&& typeof diagnostics.rawResultCount === 'number'
		&& typeof diagnostics.dedupedResultCount === 'number'
		&& typeof diagnostics.projectedTaskCount === 'number'
		&& typeof diagnostics.skippedResultCount === 'number'
		&& typeof diagnostics.fallbackUsed === 'boolean';
}

function buildSubagentToolExecutionProjection(events: SubagentProcessEvent[]) {
	const executions = new Map<string, SubagentToolExecution>();
	const orderedKeys: string[] = [];
	const consumedEventIndexes = new Set<number>();

	for (const [index, event] of events.entries()) {
		if (event.type === 'run_error') {
			const matched = matchExecutionForRunError(executions, orderedKeys, event);
			if (matched) {
				matched.error = typeof event.error === 'string' ? event.error : matched.error;
				matched.finishedAt = typeof event.timestamp === 'string' ? event.timestamp : matched.finishedAt;
				matched.status = 'completed';
				consumedEventIndexes.add(index);
			}
			continue;
		}

		if (event.type !== 'tool_call' && event.type !== 'proxy_tool_call' && event.type !== 'tool_result') {
			continue;
		}
		consumedEventIndexes.add(index);

		const key = String(event.toolCallId || `${event.runId || 'run'}:${event.name || event.label || 'tool'}:${index}`);
		if (!executions.has(key)) {
			executions.set(key, {
				key,
				toolCallId: typeof event.toolCallId === 'string' ? event.toolCallId : undefined,
				name: typeof event.name === 'string' ? event.name : undefined,
				label: typeof event.label === 'string' ? event.label : undefined,
				kind: event.type === 'proxy_tool_call' ? 'proxy_tool_call' : 'tool_call',
				status: event.type === 'tool_result' ? 'completed' : 'running',
				runId: typeof event.runId === 'string' ? event.runId : undefined,
				startedAt: typeof event.timestamp === 'string' ? event.timestamp : undefined,
			});
			orderedKeys.push(key);
		}

		const execution = executions.get(key)!;
		if (event.type === 'tool_call' || event.type === 'proxy_tool_call') {
			execution.kind = event.type === 'proxy_tool_call' ? 'proxy_tool_call' : 'tool_call';
			execution.name = execution.name || (typeof event.name === 'string' ? event.name : undefined);
			execution.label = execution.label || (typeof event.label === 'string' ? event.label : undefined);
			execution.args = event.args;
			execution.startedAt = execution.startedAt || (typeof event.timestamp === 'string' ? event.timestamp : undefined);
			continue;
		}

		execution.status = 'completed';
		execution.result = event.result;
		execution.error = typeof event.error === 'string' ? event.error : execution.error;
		execution.finishedAt = typeof event.timestamp === 'string' ? event.timestamp : execution.finishedAt;
	}

	return {
		executions: orderedKeys.map(key => executions.get(key)!).slice(0, 24),
		truncated: orderedKeys.length > 24,
		consumedEventIndexes,
	};
}

function toEvidenceSummaryArray(value: unknown): SubagentEvidenceSummary[] {
	if (!Array.isArray(value)) {
		return [];
	}

	return value.filter((item): item is SubagentEvidenceSummary => isEvidenceSummary(item));
}

function toRecordArray(value: unknown): Record<string, unknown>[] {
	if (!Array.isArray(value)) {
		return [];
	}

	return value
		.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === 'object' && !Array.isArray(item))
		.map(item => ({ ...item }));
}

function isEvidenceSummary(value: unknown): value is SubagentEvidenceSummary {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return false;
	}

	const item = value as Partial<SubagentEvidenceSummary>;
	return typeof item.kind === 'string'
		&& typeof item.title === 'string'
		&& typeof item.summary === 'string';
}

function toProcessEventArray(value: unknown): SubagentProcessEvent[] {
	if (!Array.isArray(value)) {
		return [];
	}

	return value
		.filter(item => item && typeof item === 'object' && !Array.isArray(item))
		.map(item => ({ ...(item as SubagentProcessEvent) }))
		.filter(item => typeof item.type === 'string' && item.type.trim().length > 0);
}

function toSubagentTaskPanel(value: unknown, index: number, taskValue?: Record<string, unknown>): SubagentTaskPanel | null {
	if (!value || typeof value !== 'object' || Array.isArray(value)) {
		return null;
	}

	const record = value as Record<string, unknown>;
	const task = taskValue && typeof taskValue === 'object' && !Array.isArray(taskValue) ? taskValue : {};
	const resultPayload = record.resultPayload && typeof record.resultPayload === 'object' && !Array.isArray(record.resultPayload)
		? record.resultPayload as Record<string, unknown>
		: {};
	const evidenceSummaries = [
		...toEvidenceSummaryArray(resultPayload.evidenceSummary ? [resultPayload.evidenceSummary] : []),
		...toEvidenceSummaryArray(resultPayload.evidenceSummaries),
	].slice(0, 8);
	const rawProcessEvents = toProcessEventArray(resultPayload.processEvents).slice(0, 50);
	const toolExecutionProjection = buildSubagentToolExecutionProjection(rawProcessEvents);
	const processEvents = rawProcessEvents.filter((_, eventIndex) => !toolExecutionProjection.consumedEventIndexes.has(eventIndex));
	const sortOrder = typeof record.sortOrder === 'number'
		? record.sortOrder
		: typeof task.sortOrder === 'number'
			? task.sortOrder
			: index;
	const summary = typeof record.summary === 'string'
		? record.summary
		: typeof task.summary === 'string'
			? task.summary
			: undefined;
	const goal = typeof record.goal === 'string'
		? record.goal
		: typeof task.goal === 'string'
			? task.goal
			: undefined;
	const name = typeof record.name === 'string'
		? record.name
		: typeof task.name === 'string'
			? task.name
			: typeof task.agentName === 'string'
				? task.agentName
				: undefined;
	const error = typeof record.error === 'string' ? record.error : undefined;
	const firstRunId = processEvents.find(event => typeof event.runId === 'string' && event.runId.trim())?.runId;
	const title = goal?.trim() || name?.trim() || summary?.trim() || `任务 #${sortOrder + 1}`;
	return {
		key: String(record.id ?? task.id ?? `${sortOrder}:${title}`),
		title,
		status: typeof record.status === 'string' ? record.status : undefined,
		sortOrder,
		runId: firstRunId,
		summary,
		error,
		evidenceSummaries,
		processEvents,
		toolExecutions: toolExecutionProjection.executions,
		toolExecutionsTruncated: toolExecutionProjection.truncated,
		result: record,
	};
}

// Backend-projected metadata.subagentProjection is the canonical contract.
// This parser is kept only for historical entries created before that projection existed.
function buildSyntheticProjectionDiagnostics(entry: SessionTreeEntry): SubagentProjectionDiagnostics | null {
	if (entry.type === 'subagent_result') {
		const metadata = getNormalizedSubagentResultMetadata(entry);
		if (metadata.evidenceSummaries.length === 0 && metadata.processEvents.length === 0 && metadata.finalResults.length === 0) {
			return null;
		}
		return {
			selectedSource: 'subagent_result',
			inputSources: ['subagent_result'],
			ignoredSources: [],
			rawResultCount: metadata.finalResults.length,
			dedupedResultCount: metadata.finalResults.length,
			projectedTaskCount: 0,
			skippedResultCount: 0,
			fallbackUsed: true,
			projectionPath: 'metadata-only-fallback',
		};
	}

	const collection = getLegacyPayloadDiagnostics(entry);
	if (!collection) {
		return null;
	}

	return {
		selectedSource: collection.selectedSource,
		inputSources: collection.inputSources,
		ignoredSources: collection.ignoredSources,
		rawResultCount: collection.rawResultCount,
		dedupedResultCount: collection.dedupedResultCount,
		projectedTaskCount: collection.records.length,
		skippedResultCount: collection.skippedResultCount,
		fallbackUsed: true,
		projectionPath: 'legacy-payload-fallback',
	};
}

function getLegacyPayloadDiagnostics(entry: SessionTreeEntry): {
	records: SubagentResultRecord[];
	selectedSource: SubagentProjectionDiagnostics['selectedSource'];
	inputSources: SubagentProjectionDiagnostics['inputSources'];
	ignoredSources: SubagentProjectionDiagnostics['ignoredSources'];
	rawResultCount: number;
	dedupedResultCount: number;
	skippedResultCount: number;
} | null {
	if (entry.type !== 'message') {
		return null;
	}

	const payloads: Array<{
		source: SubagentProjectionDiagnostics['selectedSource'];
		payload: { taskResults: unknown[] };
	}> = [];

	if (entry.message?.role === 'tool') {
		const payload = parseDelegatedToolPayload(entry.message.content);
		if (payload) {
			payloads.push({ source: 'tool_message', payload });
		}
	}

	const messageMetadataPayloads = getDelegatedToolPayloadsFromMessageMetadata(entry.message?.metadata);
	const entryMetadataPayloads = getDelegatedToolPayloadsFromMessageMetadata(entry.metadata);
	payloads.push(...messageMetadataPayloads.map(payload => ({ source: 'message_metadata' as const, payload })));
	if (messageMetadataPayloads.length === 0) {
		payloads.push(...entryMetadataPayloads.map(payload => ({ source: 'entry_metadata' as const, payload })));
	}

	if (payloads.length === 0) {
		return null;
	}

	const inputSources = Array.from(new Set(payloads.map(item => item.source)));
	const selectedSource = payloads[0].source;
	const ignoredSources = inputSources.filter(source => source !== selectedSource);
	const rawItems = payloads
		.filter(item => item.source === selectedSource)
		.flatMap(item => item.payload.taskResults);
	const records = rawItems
		.map(item => {
			if (!item || typeof item !== 'object' || Array.isArray(item)) return null;
			const record = item as Record<string, unknown>;
			const task = record.task && typeof record.task === 'object' && !Array.isArray(record.task)
				? record.task as Record<string, unknown>
				: undefined;
			const result = record.result && typeof record.result === 'object' && !Array.isArray(record.result)
				? record.result as Record<string, unknown>
				: undefined;
			return result ? { task, result } : null;
		})
		.filter((item): item is SubagentResultRecord => Boolean(item));
	const dedupedRecords = dedupeSubagentResultRecords(records);

	return {
		records: dedupedRecords,
		selectedSource,
		inputSources,
		ignoredSources,
		rawResultCount: rawItems.length,
		dedupedResultCount: dedupedRecords.length,
		skippedResultCount: rawItems.length - records.length,
	};
}

function getLegacySubagentResultRecords(entry: SessionTreeEntry): SubagentResultRecord[] {
	if (entry.type === 'subagent_result') {
		const content = (entry.content || {}) as SessionTreeSubagentResultContent;
		const results = Array.isArray(content.results) ? content.results : [];
		return results
			.filter((item): item is Record<string, unknown> => Boolean(item) && typeof item === 'object' && !Array.isArray(item))
			.map(result => ({ result }));
	}

	if (entry.type !== 'message') {
		return [];
	}

	const payloads: Array<{ taskResults: unknown[] }> = [];
	if (entry.message?.role === 'tool') {
		const payload = parseDelegatedToolPayload(entry.message.content);
		if (payload) payloads.push(payload);
	}

	const messageMetadataPayloads = getDelegatedToolPayloadsFromMessageMetadata(entry.message?.metadata);
	const metadataPayloads = messageMetadataPayloads.length > 0
		? messageMetadataPayloads
		: getDelegatedToolPayloadsFromMessageMetadata(entry.metadata);
	payloads.push(...metadataPayloads);

	return dedupeSubagentResultRecords(payloads
		.flatMap(payload => payload.taskResults)
		.map(item => {
			if (!item || typeof item !== 'object' || Array.isArray(item)) return null;
			const record = item as Record<string, unknown>;
			const task = record.task && typeof record.task === 'object' && !Array.isArray(record.task)
				? record.task as Record<string, unknown>
				: undefined;
			const result = record.result && typeof record.result === 'object' && !Array.isArray(record.result)
				? record.result as Record<string, unknown>
				: undefined;
			return result ? { task, result } : null;
		})
		.filter((item): item is SubagentResultRecord => Boolean(item)));
}

function dedupeSubagentResultRecords(records: SubagentResultRecord[]): SubagentResultRecord[] {
	const seen = new Set<string>();
	const unique: SubagentResultRecord[] = [];

	for (const record of records) {
		const key = subagentResultRecordKey(record);
		if (seen.has(key)) continue;
		seen.add(key);
		unique.push(record);
	}

	return unique;
}

function subagentResultRecordKey(record: SubagentResultRecord) {
	const resultId = record.result.id;
	if (typeof resultId === 'string' || typeof resultId === 'number') {
		return `result:${resultId}`;
	}

	const task = record.task || {};
	const sortOrder = typeof record.result.sortOrder === 'number'
		? record.result.sortOrder
		: typeof task.sortOrder === 'number'
			? task.sortOrder
			: '';
	const goal = typeof record.result.goal === 'string'
		? record.result.goal
		: typeof task.goal === 'string'
			? task.goal
			: '';
	const summary = typeof record.result.summary === 'string' ? record.result.summary : '';
	return `task:${sortOrder}:${goal}:${summary}`;
}

function getDelegatedToolPayloadsFromMessageMetadata(metadata: unknown): Array<{ taskResults: unknown[] }> {
	const parsed = parseJsonLikeRecord(metadata);
	if (!parsed || !Array.isArray(parsed.skillExecutions)) {
		return [];
	}

	return parsed.skillExecutions
		.flatMap(item => {
			if (!item || typeof item !== 'object' || Array.isArray(item)) return [];
			const record = item as Record<string, unknown>;
			const name = typeof record.name === 'string'
				? record.name
				: typeof record.toolName === 'string'
					? record.toolName
					: '';
			if (name !== 'delegate_parallel' && name !== 'delegate_task') return [];
			const rawResult = record.rawResult && typeof record.rawResult === 'object' && !Array.isArray(record.rawResult)
				? record.rawResult as Record<string, unknown>
				: {};
			const candidates = [
				record.result,
				record.rawResult,
				rawResult.data,
				rawResult.json,
				rawResult.text,
			];
			return candidates
				.map(candidate => parseDelegatedToolPayload(candidate))
				.filter((payload): payload is { taskResults: unknown[] } => Boolean(payload));
		});
}

function parseDelegatedToolPayload(value: unknown): { taskResults: unknown[] } | null {
	const parsed = parseJsonLikeRecord(value);
	if (!parsed) return null;
	if (parsed.delegationMode !== 'session-subagent') return null;
	if (!Array.isArray(parsed.taskResults)) return null;
	return parsed as { taskResults: unknown[] };
}

function parseJsonLikeRecord(value: unknown): Record<string, unknown> | null {
	if (value && typeof value === 'object' && !Array.isArray(value)) {
		return value as Record<string, unknown>;
	}
	if (typeof value !== 'string' || !value.trim()) {
		return null;
	}
	try {
		const parsed = JSON.parse(value);
		return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
			? parsed as Record<string, unknown>
			: null;
	} catch {
		return null;
	}
}

function matchExecutionForRunError(
	executions: Map<string, SubagentToolExecution>,
	orderedKeys: string[],
	event: SubagentProcessEvent,
) {
	if (typeof event.toolCallId === 'string' && event.toolCallId.trim()) {
		return executions.get(event.toolCallId);
	}

	for (let index = orderedKeys.length - 1; index >= 0; index -= 1) {
		const execution = executions.get(orderedKeys[index]);
		if (!execution) continue;
		if (execution.status === 'completed') continue;
		if (event.runId && execution.runId && execution.runId !== event.runId) continue;
		return execution;
	}

	return undefined;
}
