<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted, provide, reactive, ref, watch } from 'vue'
import { Icon, Input } from '@repo/ui'
import { useDebounceFn } from '@vueuse/core'
import { agent } from '@repo/api-service'

import Chat from './Chat.vue'
import NodeLog from './NodeLog.vue'
import { nodeMap } from '@/nodes'
import { useI18n } from '@/composables/useI18n'

import type { IWorkflow, IWorkflowNode } from '@repo/workflow'
import type { NodeVar } from '@/types/var'

interface Props {
	id: string
	workflow: IWorkflow
	visible: boolean
	activeTab?: 'setting' | 'last-run'
}

const props = withDefaults(defineProps<Props>(), {
	visible: false,
	id: '',
	activeTab: 'setting'
})

const emit = defineEmits<{
	'update:visible': [value: boolean]
	'update:node:data': [data: IWorkflowNode]
	'run-node': [id: string]
}>()

const { t } = useI18n()

const node = computed<IWorkflowNode>(
	() => props.workflow.nodes.find((item) => item.id === props.id)!
)

const setter = computed(() => {
	return node.value?.data?.nodeType
		? nodeMap[node.value.data.nodeType as keyof typeof nodeMap]?.Setter
		: undefined
})

const nodeInfo = computed(() => {
	return node.value?.data?.nodeType
		? nodeMap[node.value.data.nodeType as keyof typeof nodeMap]
		: undefined
})

const isLlmNode = computed(
	() => node.value?.nodeType === 'llm' || node.value?.data?.nodeType === 'llm'
)

const isImageIcon = computed(() => {
	return !!nodeInfo.value?.icon && nodeInfo.value.icon.startsWith('data:image/')
})

const closeDrawer = () => {
	emit('update:visible', false)
}

const onUpdateData = useDebounceFn((data: Record<string, unknown>) => {
	emit('update:node:data', {
		...node.value,
		data: {
			...node.value?.data,
			...data
		}
	})
}, 1000)

const name = ref(node.value?.name || '')
const remark = ref(node.value?.remark || '')
const nodeVars = ref<NodeVar[]>([])
const currentTab = ref<'setting' | 'last-run'>(props.activeTab || 'setting')
const MIN_DRAWER_WIDTH = 470
const viewportWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 1440)
const drawerWidth = ref(MIN_DRAWER_WIDTH)
const resizeState = reactive({
	startX: 0,
	startWidth: MIN_DRAWER_WIDTH,
	isDragging: false
})

const maxDrawerWidth = computed(() => {
	return Math.max(MIN_DRAWER_WIDTH, Math.floor(viewportWidth.value * 0.6))
})

const clampDrawerWidth = (width: number) => {
	return Math.min(Math.max(width, MIN_DRAWER_WIDTH), maxDrawerWidth.value)
}

const syncViewportWidth = () => {
	viewportWidth.value = window.innerWidth
	drawerWidth.value = clampDrawerWidth(drawerWidth.value)
}

const stopResize = () => {
	if (!resizeState.isDragging) return
	resizeState.isDragging = false
	document.body.style.userSelect = ''
	document.body.style.cursor = ''
	window.removeEventListener('mousemove', onResize)
	window.removeEventListener('mouseup', stopResize)
}

const onResize = (event: MouseEvent) => {
	if (!resizeState.isDragging) return
	const nextWidth = resizeState.startWidth + (resizeState.startX - event.clientX)
	drawerWidth.value = clampDrawerWidth(nextWidth)
}

const onResizeStart = (event: MouseEvent) => {
	resizeState.startX = event.clientX
	resizeState.startWidth = drawerWidth.value
	resizeState.isDragging = true
	document.body.style.userSelect = 'none'
	document.body.style.cursor = 'ew-resize'
	window.addEventListener('mousemove', onResize)
	window.addEventListener('mouseup', stopResize)
}

const onUpdateName = () => {
	if (name.value !== node.value?.name && name.value.trim() !== '') {
		emit('update:node:data', { ...node.value, name: name.value })
	}
}

const onUpdateRemark = () => {
	emit('update:node:data', { ...node.value, remark: remark.value })
}

watch(
	() => [props.id, props.visible],
	async () => {
		name.value = node.value?.name || ''
		remark.value = node.value?.remark || ''
		currentTab.value = props.activeTab || 'setting'

		if (props.id && props.visible) {
			const response = await agent.postAgentGetPrevNodeOutVariableList({
				node_id: props.id,
				varTypeList: []
			})
			nodeVars.value = (response.result as NodeVar[]) || []
		}
	}
)

watch(
	() => props.activeTab,
	(value) => {
		currentTab.value = value || 'setting'
	}
)

onMounted(() => {
	window.addEventListener('resize', syncViewportWidth)
	syncViewportWidth()
})

onBeforeUnmount(() => {
	stopResize()
	window.removeEventListener('resize', syncViewportWidth)
})

provide('nodeVars', nodeVars)
</script>

<template>
	<div class="setter">
		<div
			class="drawer shadow-2xl"
			:class="{
				'drawer--open': props.visible && setter,
				'drawer--resizing': resizeState.isDragging
			}"
			:style="{ width: `${drawerWidth}px`, maxWidth: '60vw' }"
		>
			<!-- Resize handle -->
			<div class="resize-handle" @mousedown.prevent="onResizeStart"></div>
			<header class="text-gray-800">
				<div class="w-full flex items-center justify-between">
					<h4 class="flex items-center">
						<span
							v-if="nodeInfo?.icon"
							class="h-22px w-22px flex items-center justify-center rounded-lg shrink-0"
							:style="{ background: nodeInfo?.iconColor }"
						>
							<img
								v-if="isImageIcon"
								:src="nodeInfo?.icon"
								alt="node icon"
								class="w-14px h-14px object-contain"
							/>
							<Icon v-else :icon="nodeInfo?.icon" color="#fff" :size="14" />
						</span>
						<Input
							v-model="name"
							:placeholder="t('pages.setter.titlePlaceholder')"
							variant="borderless"
							@blur="onUpdateName"
						/>
					</h4>
					<div class="flex items-center">
						<el-tooltip :content="t('pages.setter.runNode')" placement="top">
							<Icon
								icon="lucide:play"
								width="20"
								height="20"
								class="text-gray-400 p-2 hover:cursor-pointer hover:bg-gray-200"
								@click="emit('run-node', id)"
							/>
						</el-tooltip>

						<el-divider direction="vertical" />
						<Icon
							icon="lucide:x"
							height="24"
							width="24"
							@click="closeDrawer"
							class="cursor-pointer"
						/>
					</div>
				</div>
				<Input
					v-model="remark"
					:placeholder="t('pages.setter.descriptionPlaceholder')"
					variant="borderless"
					@blur="onUpdateRemark"
				/>
			</header>

			<div class="content">
				<el-tabs v-model="currentTab">
					<el-tab-pane :label="t('pages.setter.setting')" name="setting">
						<div class="tab-pane tab-pane--fill">
							<component
								:is="setter"
								:key="node?.id"
								:id="node?.id"
								:data="node?.data"
								@update="onUpdateData"
							/>
						</div>
					</el-tab-pane>
					<el-tab-pane :label="t('pages.setter.lastRun')" name="last-run">
						<div class="tab-pane tab-pane--scroll">
							<NodeLog :node="node" :active="props.visible && currentTab === 'last-run'" />
						</div>
					</el-tab-pane>
					<el-tab-pane v-if="isLlmNode" label="AI对话" name="chat">
						<Chat :node="node" :workflow-id="props.workflow.id" />
					</el-tab-pane>
				</el-tabs>
			</div>
		</div>
	</div>
</template>

<style lang="less" scoped>
.setter {
	z-index: 998;

	.drawer {
		position: fixed;
		top: 60px;
		right: 5px;
		bottom: 10px;
		min-width: 420px;
		background: #fff;
		z-index: 1000;
		border-radius: 8px;
		display: flex;
		flex-direction: column;
		border: 1px solid #e4e4e4;
		transform: translateX(110%);
		transition: transform 0.25s ease;

		&.drawer--resizing {
			transition: none;
		}

		.resize-handle {
			position: absolute;
			left: -4px;
			top: 50%;
			transform: translateY(-50%);
			width: 3px;
			height: 32px;
			background-color: #c1c4cb;
			border-radius: 8px;
			cursor: ew-resize;
		}
	}

	.drawer--open {
		transform: translateX(0);
	}

	.drawer header {
		height: 66px;
		padding: 16px;
		padding-bottom: 0;

		h4 {
			margin: 0;
		}
	}

	.drawer .content {
		flex: 1;
		min-height: 0;
		overflow: hidden;
	}

	:deep(.el-collapse-item__header) {
		box-sizing: border-box;
		padding: 0 8px;
	}

	:deep(.el-collapse-item__content) {
		box-sizing: border-box;
		padding: 0 8px 12px;
	}

	:deep(.el-tabs__nav-scroll) {
		padding-left: 20px;
	}

	:deep(.el-tabs) {
		height: 100%;
		display: flex;
		flex-direction: column;
	}

	:deep(.el-tabs__header) {
		flex-shrink: 0;
		margin-bottom: 0;
	}

	:deep(.el-tabs__content) {
		flex: 1;
		min-height: 0;
		overflow: hidden;
	}

	:deep(.el-tab-pane) {
		height: 100%;
	}

	.tab-pane {
		height: 100%;
		min-height: 0;
		margin-top: 16px;
	}

	.tab-pane--fill {
		display: flex;
		flex-direction: column;
		overflow-x: hidden;
		overflow-y: auto;
	}

	:deep(.tab-pane--fill > .el-scrollbar) {
		height: 100%;
	}

	.tab-pane--scroll {
		overflow-y: auto;
	}
}
</style>
