<script setup lang="ts">
import { computed, ref } from 'vue'
import { Icon } from '@repo/ui'

import { useI18n } from '@/composables/useI18n'
import { nodeMap } from '@/nodes'
import { getNodeDisplayName } from '@/nodes/i18n'

import type { BubbleMessage } from '@/views/chat/types'
import type { RunnerNodeState } from '@/store/modules/runner.store'

const props = defineProps<{
	message: BubbleMessage
}>()

const { t } = useI18n()

const workflowTraceNodes = computed<RunnerNodeState[]>(() =>
	Array.isArray(props.message.workflowTraceNodes) ? props.message.workflowTraceNodes : []
)

const shouldShowWorkflowTrace = computed(
	() =>
		props.message.role === 'ai' &&
		(props.message.workflowTraceVisible || workflowTraceNodes.value.length > 0)
)

const isWorkflowTraceOpen = ref(false)

const toggleWorkflowTrace = () => {
	isWorkflowTraceOpen.value = !isWorkflowTraceOpen.value
}

const workflowExpandedNodeIds = computed<string[]>(() =>
	Array.isArray(props.message.workflowExpandedNodeIds)
		? props.message.workflowExpandedNodeIds.filter(
				(item): item is string => typeof item === 'string'
			)
		: []
)

const isWorkflowNodeOpen = (nodeId: string) => workflowExpandedNodeIds.value.includes(nodeId)

const handleWorkflowCollapseChange = (value: string | string[]) => {
	props.message.workflowExpandedNodeIds = Array.isArray(value) ? value : value ? [value] : []
}

const workflowTraceStatus = computed(() => {
	const nodes = workflowTraceNodes.value
	if (nodes.some((node) => node.status === 'failed')) return 'failed'
	if (nodes.some((node) => node.status === 'suspended')) return 'suspended'
	if (nodes.some((node) => node.status === 'running')) return 'running'
	if (nodes.length && nodes.every((node) => node.status === 'success')) return 'success'
	return props.message.streamCompleted ? 'success' : 'running'
})

const workflowTraceStatusClass = computed(() => `is-${workflowTraceStatus.value}`)

const workflowTraceStatusIcon = computed(() => {
	const status = workflowTraceStatus.value
	if (status === 'success') return 'lucide:check'
	if (status === 'failed') return 'lucide:x'
	if (status === 'suspended') return 'lucide:pause'
	return 'lucide:loader'
})

const nodeStatusClass = (status?: string | null) => {
	if (status === 'success') return 'is-success'
	if (status === 'failed') return 'is-failed'
	if (status === 'running') return 'is-running'
	if (status === 'suspended') return 'is-suspended'
	return 'is-idle'
}

const nodeStatusIcon = (status?: string | null) => {
	if (status === 'success') return 'lucide:check'
	if (status === 'failed') return 'lucide:x'
	if (status === 'suspended') return 'lucide:pause'
	if (status === 'running') return 'lucide:loader'
	return 'lucide:circle'
}

const nodeIcon = (node: RunnerNodeState) => nodeMap[node.nodeType]?.icon || 'lucide:box'

const isImageIcon = (icon?: string) => !!icon && icon.startsWith('data:image/')

const nodeDisplayName = (node: RunnerNodeState) =>
	node.nodeName ||
	getNodeDisplayName(node.nodeType) ||
	nodeMap[node.nodeType]?.displayName ||
	node.nodeType ||
	t('pages.runWorkflow.tracePanel.unnamedNode')

const nodeUseTime = (node: RunnerNodeState) => {
	const useTime = Number(node.track?.use_time ?? node.track?.useTime ?? 0)
	if (Number.isFinite(useTime) && useTime > 0) {
		return `${useTime} ms`
	}
	return '-'
}

const formatWorkflowValue = (value: unknown) => {
	if (value === undefined || value === null || value === '') return '{}'
	if (typeof value === 'string') return value
	try {
		return JSON.stringify(value, null, 2)
	} catch {
		return String(value)
	}
}
</script>

<template>
	<div v-if="shouldShowWorkflowTrace" class="workflow-trace-bubble">
		<div
			type="button"
			class="workflow-trace-bubble__header"
			@click.stop.prevent="toggleWorkflowTrace"
		>
			<span class="workflow-trace-bubble__status" :class="workflowTraceStatusClass">
				<Icon :icon="workflowTraceStatusIcon" :width="12" />
			</span>
			<span class="workflow-trace-bubble__title">
				{{ t('pages.runWorkflow.chatPanel.workflow') }}
			</span>
			<Icon
				icon="lucide:chevron-down"
				class="workflow-trace-bubble__chevron"
				:class="{ 'is-open': isWorkflowTraceOpen }"
				:width="18"
			/>
		</div>

		<div v-if="isWorkflowTraceOpen" class="workflow-node-list">
			<div v-if="!workflowTraceNodes.length" class="workflow-node-empty">
				{{ t('pages.runWorkflow.chatPanel.nodeRunEmpty') }}
			</div>
			<el-collapse
				v-else
				class="workflow-node-collapse"
				:model-value="workflowExpandedNodeIds"
				@update:model-value="handleWorkflowCollapseChange"
			>
				<el-collapse-item
					v-for="node in workflowTraceNodes"
					:key="node.nodeId"
					:name="node.nodeId"
					class="workflow-node-card"
				>
					<template #title>
						<div class="workflow-node-card__summary">
							<Icon
								icon="lucide:chevron-right"
								class="workflow-node-card__chevron"
								:class="{ 'is-open': isWorkflowNodeOpen(node.nodeId) }"
								:width="14"
							/>
							<span class="workflow-node-card__icon">
								<img v-if="isImageIcon(nodeIcon(node))" :src="nodeIcon(node)" alt="node icon" />
								<Icon v-else :icon="nodeIcon(node)" :width="14" />
							</span>
							<span class="workflow-node-card__name">{{ nodeDisplayName(node) }}</span>
							<span class="workflow-node-card__duration">{{ nodeUseTime(node) }}</span>
							<span class="workflow-node-card__status" :class="nodeStatusClass(node.status)">
								<Icon :icon="nodeStatusIcon(node.status)" :width="12" />
							</span>
						</div>
					</template>

					<div class="workflow-node-card__detail">
						<div class="workflow-json-panel">
							<div class="workflow-json-panel__title">
								{{ t('pages.runWorkflow.tracePanel.input') }}
							</div>
							<CodeEditor
								:model-value="formatWorkflowValue(node.track?.input_variable) || '-'"
								language="json"
								:tools="true"
								:read-only="true"
								:allow-change-language="false"
								:height="180"
							/>
						</div>

						<div class="workflow-json-panel">
							<div class="workflow-json-panel__title">
								{{ t('pages.runWorkflow.tracePanel.output') }}
							</div>
							<CodeEditor
								:model-value="formatWorkflowValue(node.track?.output_variable) || '-'"
								language="json"
								:tools="true"
								:read-only="true"
								:allow-change-language="false"
								:height="180"
							/>
						</div>
					</div>
				</el-collapse-item>
			</el-collapse>
		</div>
	</div>
</template>

<style lang="less" scoped>
.workflow-trace-bubble {
	box-sizing: border-box;
	width: 100%;
	padding: 12px 14px;
	border-radius: 8px;
	border: 1px solid #b9efd2;
	background: #e8fbf1;
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

.workflow-trace-bubble__header {
	width: 100%;
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 0;
	border: none;
	background: transparent;
	color: #24364b;
	cursor: pointer;
	text-align: left;
}

.workflow-trace-bubble__status,
.workflow-node-card__status {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 14px;
	height: 14px;
	border-radius: 999px;
	color: #fff;
	background: #11a260;
	flex-shrink: 0;
}

.workflow-trace-bubble__status.is-running,
.workflow-node-card__status.is-running {
	background: #2388ff;
}

.workflow-trace-bubble__status.is-failed,
.workflow-node-card__status.is-failed {
	background: #f04438;
}

.workflow-trace-bubble__status.is-suspended,
.workflow-node-card__status.is-suspended {
	background: #667085;
}

.workflow-trace-bubble__title {
	font-size: 14px;
	font-weight: 700;
	color: #24364b;
	line-height: 24px;
}

.workflow-trace-bubble__chevron {
	margin-left: auto;
	color: #667085;
	transition: transform 0.16s ease;
}

.workflow-trace-bubble__chevron.is-open {
	transform: rotate(180deg);
}

.workflow-node-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin-top: 14px;
	width: 100%;
}

.workflow-node-collapse {
	display: flex;
	flex-direction: column;
	gap: 6px;
	border: none;
}

.workflow-node-empty {
	padding: 14px 16px;
	border-radius: 8px;
	background: rgba(255, 255, 255, 0.82);
	color: var(--text-tertiary);
	line-height: 1.5;
}

.workflow-node-card {
	box-sizing: border-box;
	width: 100%;
	border-radius: 8px;
	background: #fff;
	border: 1px solid #e6eaf0;
	box-shadow: 0 2px 8px rgba(16, 24, 40, 0.08);
	overflow: hidden;

	:deep(.el-collapse-item__header) {
		height: auto;
		min-height: 48px;
		padding: 0;
		border: none;
		background: #fff;
		line-height: normal;
	}

	:deep(.el-collapse-item__arrow) {
		display: none;
	}

	:deep(.el-collapse-item__wrap) {
		border: none;
	}

	:deep(.el-collapse-item__content) {
		padding-bottom: 0;
	}
}

.workflow-node-card__summary {
	box-sizing: border-box;
	width: 100%;
	display: flex;
	align-items: center;
	gap: 10px;
	min-height: 48px;
	padding: 8px 12px;
	border: none;
	background: #fff;
	cursor: pointer;
	text-align: left;
}

.workflow-node-card__chevron {
	color: #98a2b3;
	transition: transform 0.16s ease;
	flex-shrink: 0;
}

.workflow-node-card__chevron.is-open {
	transform: rotate(90deg);
}

.workflow-node-card__icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 22px;
	height: 22px;
	border-radius: 8px;
	background: #1677ff;
	color: #fff;
	flex-shrink: 0;
}

.workflow-node-card__icon img {
	width: 18px;
	height: 18px;
	object-fit: contain;
}

.workflow-node-card__name {
	min-width: 0;
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 12px;
	font-weight: 700;
	color: #24364b;
}

.workflow-node-card__duration {
	color: #667085;
	font-size: 14px;
	white-space: nowrap;
}

.workflow-node-card__status {
	width: 18px;
	height: 18px;
}

.workflow-node-card__status.is-idle {
	background: #98a2b3;
}

.workflow-node-card__detail {
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 0 8px 8px;
	width: 100%;
}

.workflow-json-panel {
	box-sizing: border-box;
	width: 100%;
	padding: 10px 12px;
	border-radius: 8px;
	background: #f1f4f8;
	color: #24364b;
}

.workflow-json-panel__title {
	margin-bottom: 8px;
	font-size: 14px;
	font-weight: 700;
	color: #24364b;
}

.workflow-json-panel pre {
	min-height: 96px;
	max-height: 260px;
	margin: 0;
	padding: 8px 10px;
	overflow: auto;
	border-radius: 4px;
	background: #eef2f7;
	border-top: 2px solid #4b5563;
	color: #003b8f;
	font-size: 13px;
	line-height: 1.55;
	white-space: pre-wrap;
	word-break: break-word;
}
</style>
