import type { RuntimeProcessEventProjection } from '../types/index.d';

export interface ToolProcessDisplayEvent {
	key: string;
	event: RuntimeProcessEventProjection;
	label: string;
	detail?: string;
	level?: RuntimeProcessEventProjection['level'];
	current?: number;
	total?: number;
	percent?: number;
}

const LABELS = {
	creatingWorkspace: '\u521b\u5efa\u68c0\u6d4b\u5de5\u4f5c\u533a',
	workspaceReady: '\u68c0\u6d4b\u5de5\u4f5c\u533a\u5df2\u51c6\u5907',
	videoPathResolved: '\u5df2\u5b9a\u4f4d\u89c6\u9891\u6587\u4ef6',
	extractingFrames: '\u6b63\u5728\u62bd\u53d6\u89c6\u9891\u5e27',
	framesExtracted: '\u89c6\u9891\u62bd\u5e27\u5b8c\u6210',
	resolvingModelConfig: '\u6b63\u5728\u8bfb\u53d6\u6a21\u578b\u914d\u7f6e',
	detectingDamageCandidates: '\u5f00\u59cb\u68c0\u6d4b\u7591\u4f3c\u65e7\u4f24',
	batchStarted: '\u5f00\u59cb\u5206\u6790\u89c6\u9891\u5206\u6bb5',
	batchCompleted: '\u89c6\u9891\u5206\u6bb5\u5206\u6790\u5b8c\u6210',
	batchFailed: '\u89c6\u9891\u5206\u6bb5\u5206\u6790\u5931\u8d25',
	detectionComplete: '\u7591\u4f3c\u65e7\u4f24\u68c0\u6d4b\u5b8c\u6210',
	groundingCandidates: '\u6b63\u5728\u5b9a\u4f4d\u65e7\u4f24\u4f4d\u7f6e',
	groundingComplete: '\u65e7\u4f24\u4f4d\u7f6e\u5b9a\u4f4d\u5b8c\u6210',
	selectingBestFrames: '\u6b63\u5728\u7b5b\u9009\u6700\u4f73\u8bc1\u636e\u5e27',
	bestFrameSelectionComplete: '\u6700\u4f73\u8bc1\u636e\u5e27\u7b5b\u9009\u5b8c\u6210',
	fullInspectionComplete: '\u65e7\u4f24\u68c0\u6d4b\u5b8c\u6210',
	framesOnlyComplete: '\u62bd\u5e27\u6a21\u5f0f\u5b8c\u6210',
	detectOnlyComplete: '\u5019\u9009\u68c0\u6d4b\u6a21\u5f0f\u5b8c\u6210',
	noCandidatesFound: '\u672a\u53d1\u73b0\u7591\u4f3c\u65e7\u4f24',
	requestStarted: '\u6a21\u578b\u5206\u6790\u4e2d',
	requestCompleted: '\u6a21\u578b\u5206\u6790\u5b8c\u6210',
	requestRetrying: '\u6a21\u578b\u8bf7\u6c42\u5931\u8d25\uff0c\u6b63\u5728\u91cd\u8bd5',
	requestFailed: '\u6a21\u578b\u8bf7\u6c42\u5931\u8d25',
	requestMalformed: '\u6a21\u578b\u8fd4\u56de\u683c\u5f0f\u5f02\u5e38',
	segmentPrefix: '\u5f00\u59cb\u5206\u6790\u7b2c',
	segmentSuffix: '\u6bb5\u89c6\u9891',
	segmentDonePrefix: '\u7b2c',
	segmentDoneSuffix: '\u6bb5\u5206\u6790\u5b8c\u6210',
	foundPrefix: '\u53d1\u73b0',
	damageUnit: '\u4e2a\u7591\u4f3c\u65e7\u4f24',
	totalProgress: '\u603b\u8fdb\u5ea6',
	frameTotalPrefix: '\u5171',
	frameUnit: '\u5e27',
	batchSizePrefix: '\u6bcf\u6279',
	candidateUnit: '\u4e2a\u5019\u9009\u70b9',
	pendingGroundingPrefix: '\u5f85\u5b9a\u4f4d',
	annotationPrefix: '\u751f\u6210',
	annotationUnit: '\u4e2a\u5b9a\u4f4d\u7ed3\u679c',
	confirmedPrefix: '\u786e\u8ba4',
	confirmedUnit: '\u5904\u65e7\u4f24',
	frameRangePrefix: '\u5e27',
	separator: '\uff0c',
};

const MESSAGE_LABELS: Record<string, string> = {
	'creating workspace': LABELS.creatingWorkspace,
	'workspace ready': LABELS.workspaceReady,
	'video path resolved': LABELS.videoPathResolved,
	'extracting frames': LABELS.extractingFrames,
	'frames extracted': LABELS.framesExtracted,
	'resolving model config': LABELS.resolvingModelConfig,
	'detecting damage candidates': LABELS.detectingDamageCandidates,
	'batch started': LABELS.batchStarted,
	'batch completed': LABELS.batchCompleted,
	'batch failed': LABELS.batchFailed,
	'damage candidate detection complete': LABELS.detectionComplete,
	'grounding damage candidates': LABELS.groundingCandidates,
	'grounding complete': LABELS.groundingComplete,
	'selecting best frames': LABELS.selectingBestFrames,
	'best frame selection complete': LABELS.bestFrameSelectionComplete,
	'full inspection complete': LABELS.fullInspectionComplete,
	'frames-only mode complete': LABELS.framesOnlyComplete,
	'detect-only mode complete': LABELS.detectOnlyComplete,
	'no candidates found': LABELS.noCandidatesFound,
	'request started': LABELS.requestStarted,
	'request completed': LABELS.requestCompleted,
	'request failed, retrying': LABELS.requestRetrying,
	'request failed': LABELS.requestFailed,
	'request returned malformed response': LABELS.requestMalformed,
};

const LOW_VALUE_MESSAGES = new Set([
	'request started',
	'request completed',
	'batch started',
]);

export function buildToolProcessDisplay(events: RuntimeProcessEventProjection[] | undefined) {
	const source = Array.isArray(events) ? events : [];
	const batchStats = buildBatchStats(source);
	const timeline = source
		.filter(event => shouldShowTimelineEvent(event))
		.map(event => formatProcessEvent(event, batchStats));
	const latest = pickLatestDisplayEvent(source, batchStats);
	return { latest, timeline };
}

export function formatProcessEvent(
	event: RuntimeProcessEventProjection | undefined,
	batchStats = buildBatchStats(event ? [event] : []),
): ToolProcessDisplayEvent | undefined {
	if (!event) return undefined;
	const payload = event.payload || {};
	const baseLabel = MESSAGE_LABELS[event.message] || event.message;
	const batch = numberValue(payload.batch);
	const totalBatches = numberValue(payload.totalBatches) ?? event.total;
	const completedBatchCount = totalBatches ? batchStats.completed.size : undefined;

	let label = baseLabel;
	let detail = event.detail;
	let current = event.current;
	let total = event.total;
	let percent = event.percent;

	if (event.message === 'batch started' && batch && totalBatches) {
		label = `${LABELS.segmentPrefix} ${batch}/${totalBatches} ${LABELS.segmentSuffix}`;
		detail = formatFrameRange(payload);
		current = completedBatchCount;
		total = totalBatches;
		percent = percentFrom(completedBatchCount, totalBatches);
	} else if (event.message === 'batch completed' && batch && totalBatches) {
		label = `${LABELS.segmentDonePrefix} ${batch}/${totalBatches} ${LABELS.segmentDoneSuffix}`;
		const damageCount = numberValue(payload.damageCount);
		const parts = [
			damageCount !== undefined ? `${LABELS.foundPrefix} ${damageCount} ${LABELS.damageUnit}` : undefined,
			`${LABELS.totalProgress} ${completedBatchCount ?? batchStats.completed.size}/${totalBatches}`,
		].filter(Boolean);
		detail = parts.join(LABELS.separator);
		current = completedBatchCount;
		total = totalBatches;
		percent = percentFrom(completedBatchCount, totalBatches);
	} else if (event.message === 'detecting damage candidates') {
		const frameCount = numberValue(payload.frameCount);
		const batchSize = numberValue(payload.batchSize);
		label = LABELS.detectingDamageCandidates;
		detail = [
			frameCount ? `${LABELS.frameTotalPrefix} ${frameCount} ${LABELS.frameUnit}` : undefined,
			batchSize ? `${LABELS.batchSizePrefix} ${batchSize} ${LABELS.frameUnit}` : undefined,
		].filter(Boolean).join(LABELS.separator) || undefined;
	} else if (event.message === 'frames extracted') {
		const frameCount = numberValue(payload.frameCount);
		const resolution = stringValue(payload.resolution);
		label = LABELS.framesExtracted;
		detail = [
			frameCount ? `${frameCount} ${LABELS.frameUnit}` : undefined,
			resolution,
		].filter(Boolean).join(LABELS.separator) || undefined;
	} else if (event.message === 'damage candidate detection complete') {
		const candidateCount = numberValue(payload.candidateCount);
		label = LABELS.detectionComplete;
		detail = candidateCount !== undefined ? `${LABELS.foundPrefix} ${candidateCount} ${LABELS.candidateUnit}` : undefined;
		current = batchStats.total || current;
		total = batchStats.total || total;
		percent = batchStats.total ? 100 : percent;
	} else if (event.message === 'grounding damage candidates') {
		const candidateCount = numberValue(payload.candidateCount);
		label = LABELS.groundingCandidates;
		detail = candidateCount !== undefined ? `${LABELS.pendingGroundingPrefix} ${candidateCount} ${LABELS.candidateUnit}` : undefined;
	} else if (event.message === 'grounding complete') {
		const annotationCount = numberValue(payload.annotationCount);
		label = LABELS.groundingComplete;
		detail = annotationCount !== undefined ? `${LABELS.annotationPrefix} ${annotationCount} ${LABELS.annotationUnit}` : undefined;
	} else if (event.message === 'best frame selection complete') {
		const damageCount = numberValue(payload.damageCount);
		label = LABELS.bestFrameSelectionComplete;
		detail = damageCount !== undefined ? `${LABELS.confirmedPrefix} ${damageCount} ${LABELS.confirmedUnit}` : undefined;
	}

	return {
		key: `${event.operationId}-${event.sequence}-${event.timestamp}`,
		event,
		label,
		detail,
		level: event.level,
		current,
		total,
		percent,
	};
}

function pickLatestDisplayEvent(
	events: RuntimeProcessEventProjection[],
	batchStats: ReturnType<typeof buildBatchStats>,
): ToolProcessDisplayEvent | undefined {
	for (let index = events.length - 1; index >= 0; index -= 1) {
		const event = events[index];
		if (!shouldShowMainEvent(event)) continue;
		return formatProcessEvent(event, batchStats);
	}
	return events.length ? formatProcessEvent(events[events.length - 1], batchStats) : undefined;
}

function shouldShowMainEvent(event: RuntimeProcessEventProjection) {
	if (event.level === 'error' || event.status === 'failed') return true;
	return !LOW_VALUE_MESSAGES.has(event.message);
}

function shouldShowTimelineEvent(event: RuntimeProcessEventProjection) {
	if (event.level === 'error' || event.status === 'failed') return true;
	return !LOW_VALUE_MESSAGES.has(event.message);
}

function buildBatchStats(events: RuntimeProcessEventProjection[]) {
	const completed = new Set<number>();
	let total = 0;
	for (const event of events) {
		const payload = event.payload || {};
		const batch = numberValue(payload.batch);
		const totalBatches = numberValue(payload.totalBatches) ?? event.total;
		if (totalBatches && totalBatches > total) total = totalBatches;
		if (event.message === 'batch completed' && batch) completed.add(batch);
	}
	return { completed, total };
}

function percentFrom(current?: number, total?: number) {
	if (typeof current !== 'number' || typeof total !== 'number' || total <= 0) return undefined;
	return Math.max(0, Math.min(100, Math.round((current / total) * 100)));
}

function numberValue(value: unknown): number | undefined {
	return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
}

function stringValue(value: unknown): string | undefined {
	return typeof value === 'string' && value.trim() ? value : undefined;
}

function formatFrameRange(payload: Record<string, unknown>) {
	const start = numberValue(payload.frameStart);
	const end = numberValue(payload.frameEnd);
	if (!start || !end) return undefined;
	return `${LABELS.frameRangePrefix} ${start}-${end}`;
}
