<!--
 * 文件说明：通用节点参数面板，配置输入输出及机器人动作调试。
 * 作用范围：仅服务于流程设计器模块。
 -->
<template>
    <el-collapse v-model="activeNames">
        <!-- 输入参数 -->
        <el-collapse-item name="1" icon-position="left">
            <template #title>
                输入参数
                <el-button :circle="true" size="small" @click="(e) => addFormItem(e, 'nodeParams')" class="addFormItem">
                    <el-icon :size="14">
                        <Plus />
                    </el-icon>
                </el-button>
            </template>
            <div class="form__container">
                <el-button type="primary" v-if="hasBindableDevice" size="small" @click="openRobotForm(props.data.properties.action)">{{ robotActionButtonText }}</el-button>
                <el-form :inline="true" :model="formData" :rules="rules" ref="dynamicFormRef" label-position="top"
                    label-width="auto">
                    <div v-for="(property, index) in formData.nodeParams" :key="index">
                        <el-row>
                            <el-form-item :label="index === 0 ? '参数名' : ''" :prop="`nodeParams.${index}.name`"
                                :rules="[{ required: true, message: '请输入参数名', trigger: 'blur' }]">
                                <el-input :disabled="property?.disabled || false" class="param-name"
                                    v-model="property.name" placeholder="请输入" clearable />
                            </el-form-item>
                            <el-form-item :label="index === 0 ? '参数值' : ''" :prop="`nodeParams.${index}.type`">
                                <el-select v-model="property.type" class="param-type" :disabled="property?.forceSelect || false" @change="handleTypeChange(index, formData, property.type)">
                                    <el-option label="引用" value="quote" />
                                    <el-option label="输入" value="input" />
                                </el-select>
                                <el-form-item v-if="property.type === 'input'"
                                    :rules="testRule(property)"
                                    :prop="`nodeParams.${index}.input`">
                                    <el-input-number class="param-value" v-if="property.componentType === 'number'"
                                        v-model="property.input" :min="0" :max="property.max || Infinity"
                                        :controls="property?.controls || true" :step-strictly="true" :step="property.step || 1" placeholder="请输入" clearable />
                                    <el-select v-model="property.input" class="param-value"
                                        v-else-if="property.componentType === 'select'">
                                        <el-option v-for="item in property.selectOptions" :key="item.value"
                                            :label="item.label" :value="item.value" />
                                    </el-select>
                                    <el-input class="param-value" v-else v-model="property.input" placeholder="请输入"
                                        clearable />
                                </el-form-item>
                                <el-form-item v-if="property.type === 'quote'"
                                    :rules="[{ required: true, message: '请选择参数值', trigger: 'blur' }]"
                                    :prop="`nodeParams.${index}.quote`">
                                    <el-cascader :ref="(el) => { if (el) cascaderRefs[index] = el }"
                                        v-model="property.quote" :checkStrictly="true" :options="quoteOptions"
                                        placeholder="请选择"
                                        @visible-change="(visible) => visibleChange(visible, index, property.quote)"
                                        @change="(value) => cascaderChange(value, index, formData, 'nodeParams')" />
                                </el-form-item>
                            </el-form-item>
                            <el-button :icon="Minus" circle size="small" :disabled="property?.disabled || false"
                                @click="handleDelete(index)" class="deleteBtn" />
                        </el-row>
                    </div>
                </el-form>
            </div>
        </el-collapse-item>

        <!-- 输出参数 -->
        <el-collapse-item name="2" icon-position="left">
            <template #title>
                输出参数
                <el-button :circle="true" size="small" @click="(e) => addFormItem(e, 'outputParams')"
                    class="addFormItem">
                    <el-icon :size="14">
                        <Plus />
                    </el-icon>
                </el-button>
            </template>
            <div class="form__container">
                <el-form :inline="true" :model="formData" label-position="top" label-width="auto" :rules="outputRules"
                    ref="outputFormRef">
                    <FormItemRecursive formType="output" :current-list="formData.outputParams" prop-path="outputParams"
                        :depth="0" :is-first-level="true" :endDepth="2" :parent-path="[]"
                        @delete-item="(path) => deleteTopLevelItem(path, 'outputParams')" />
                </el-form>
            </div>
        </el-collapse-item>
    </el-collapse>

     <el-dialog
        v-model="dialogVisible"
        :title="robotDialogTitle"
        width="460"
    >
        <div class="robot-device-tip">设备来自机器人最近一次在线上报，单个匹配设备会自动锁定。</div>
        <el-form ref="robotFormRef" :model="robotForm" :rules="robotFormRules" label-width="90px">
            <el-form-item label="机器人" prop="robotId">
                <el-select v-model="robotForm.robotId" filterable placeholder="请选择在线机器人"
                    style="width: 100%" @change="loadRobotDevices">
                    <el-option v-for="item in robotOptions" :key="item.value"
                        :label="item.label" :value="item.value" />
                </el-select>
            </el-form-item>
            <el-form-item label="执行设备" prop="deviceId">
                <el-select v-model="robotForm.deviceId" filterable :loading="deviceLoading"
                    :disabled="deviceOptions.length === 1" placeholder="请先选择机器人" style="width: 100%">
                    <el-option v-for="item in deviceOptions" :key="item.value"
                        :label="item.label" :value="item.value" />
                </el-select>
                <div v-if="deviceOptions.length === 1" class="device-auto-hint">已自动匹配并锁定</div>
            </el-form-item>
        </el-form>
        <template #footer>
            <el-button @click="dialogVisible = false">取消</el-button>
            <el-button type="primary" :loading="stationListLoading" @click="submitRobotForm">确认</el-button>
        </template>
    </el-dialog>
</template>

<script setup>
import { computed, ref, reactive, watch, nextTick } from 'vue'
import { Plus, Minus } from '@element-plus/icons-vue'
import FormItemRecursive from '../FormItemRecursive.vue'
import { getInput } from '@/utils/flow'
import { useQuote } from './useQuote.js'
import { getArmStatus, getArmJointState, getAgvStatus, getAgvStations } from '@/api/flow/flow'
import { getRobotList, getRobotDevicesByRobotId } from '@/api/inspection/robot'
import { ElMessage } from 'element-plus'
import { getDeviceKindForAction, toDeviceOptions } from '@/utils/robotDevice'

const props = defineProps({
    data: Object
})

const emit = defineEmits(['save-success', 'save-error'])

const formData = reactive({
    nodeParams: [],
    outputParams: []
})

const rules = reactive({})
const outputRules = reactive({})
const dynamicFormRef = ref()
const outputFormRef = ref()
const activeNames = ref(['1', '2'])

// 使用共享的 quote 逻辑
const { quoteOptions, cascaderRefs, handleTypeChange, cascaderChange, visibleChange } = useQuote(props.data.id)

// 添加表单项
/**
 * 新增 addFormItem 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} e 触发操作的浏览器事件
 * @param {*} type 目标类型
 */
const addFormItem = (e, type) => {
    e.stopPropagation()
    const obj = {
        name: '',
        type: type === 'nodeParams' ? 'input' : 'string',
        required: false,
        children: [],
        input: ''
    }
    if (!formData[type]) formData[type] = []
    formData[type].push(obj)
}

// 删除输入参数（非递归）
/**
 * 处理 handleDelete 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} index 目标项索引
 */
const handleDelete = (index) => {
    formData.nodeParams.splice(index, 1)
}

// 删除顶层输出参数（递归删除通过 FormItemRecursive 的 emit 处理）
/**
 * 删除 deleteTopLevelItem 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} fullPath 字段完整路径
 * @param {*} propPath 字段校验路径
 */
const deleteTopLevelItem = (fullPath, propPath) => {
    let currentLevel = formData[propPath]
    for (let i = 0; i < fullPath.length - 1; i++) {
        currentLevel = currentLevel[fullPath[i]].children
    }
    const lastIndex = fullPath[fullPath.length - 1]
    currentLevel.splice(lastIndex, 1)
}

// 初始化数据
/**
 * 初始化 initData 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const initData = () => {
    const nodeParams = JSON.parse(JSON.stringify(props.data.properties.nodeParams || []))
    const outputParams = JSON.parse(JSON.stringify(props.data.properties.outputParams || []))
    formData.nodeParams = nodeParams;
    formData.outputParams = outputParams;
}

watch(
    () => props.data,
    () => {
        initData()
    },
    { immediate: true, deep: true }
)

// 保存验证
/**
 * 校验 validateAndSave 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const validateAndSave = async () => {
    let inputValid = true
    let outputValid = true
    if (dynamicFormRef.value) {
        await dynamicFormRef.value.validate((valid) => { if (!valid) inputValid = false })
    }
    if (outputFormRef.value) {
        await outputFormRef.value.validate((valid) => { if (!valid) outputValid = false })
    }

    // 保存到 lf
    lf.setProperties(props.data.id, {
        ...props.data.properties,
        ...formData
    })

    if (inputValid && outputValid) {
        emit('save-success')
    } else {
        emit('save-error')
    }
}

const dialogVisible = ref(false)
const robotFormRef = ref(null)
const robotFormRules = reactive({
    robotId: [{ required: true, message: '请选择机器人', trigger: 'change' }],
    deviceId: [{ required: true, message: '设备ID不能为空', trigger: 'blur' }]
})
const robotForm = reactive({
    deviceId: '',
    robotId: ''
})
const robotOptions = ref([])
const deviceOptions = ref([])
const deviceLoading = ref(false)

/**
 * 加载 loadRobotOptions 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const loadRobotOptions = async () => {
    const response = await getRobotList({ pageNum: 1, pageSize: 1000, connectStatus: '1' })
    robotOptions.value = (response.rows || []).map(robot => ({
        value: robot.robotId,
        label: `${robot.robotName || robot.robotId} (${robot.robotId})`
    }))
}

/**
 * 加载 loadRobotDevices 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} robotId 机器人 ID
 */
const loadRobotDevices = async (robotId) => {
    robotForm.deviceId = ''
    deviceOptions.value = []
    if (!robotId) return
    deviceLoading.value = true
    try {
        const response = await getRobotDevicesByRobotId(robotId, {
            deviceKind: getDeviceKindForAction(robotFormAction.value)
        })
        const devices = Array.isArray(response.data) ? response.data : []
        deviceOptions.value = toDeviceOptions(devices)
        if (devices.length > 0) robotForm.deviceId = devices[0].deviceId
        if (devices.length === 0) ElMessage.warning('当前机器人没有匹配的在线设备')
    } finally {
        deviceLoading.value = false
    }
}

/**
 * 测试 testRule 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} property 参数属性
 */
const testRule = (property) => {
    if (property?.name === 'stationId' && props.data.properties.action === 'AGV_MOVE_TO_STATION') {
        return [
            { required: true, message: '请从站点列表中选择站点', trigger: 'change' },
            { validator: () => {
                const deviceId = formData.nodeParams.find(param => param.name === 'deviceId')?.input
                const selectedFromList = property.selectOptions?.some(option => option.value === property.input)
                if (selectedFromList && property.stationDeviceId === deviceId) return Promise.resolve()
                return Promise.reject(new Error('设备已变化，请重新加载站点列表'))
            }, trigger: 'change' }
        ]
    }

    if (property?.name === 'acceleration') {
        return [
            { required: true, message: '请输入参数值', trigger: 'blur' },
            { validator: () => {
                if (formData.nodeParams.find(param => param.name === 'acceleration')?.input > formData.nodeParams.find(param => param.name === 'velocity')?.input) {
                    return Promise.resolve()
                } else {
                    return Promise.reject(new Error('加速度必须大于速度'))
                }
            }, trigger: 'change' }
        ]

    }

    if (property?.name === 'velocity') {
        return [
            { required: true, message: '请输入参数值', trigger: 'blur' },
            { validator: () => {
                if (formData.nodeParams.find(param => param.name === 'acceleration')?.input > formData.nodeParams.find(param => param.name === 'velocity')?.input) {
                    return Promise.resolve()
                } else {
                    return Promise.reject(new Error('加速度必须大于速度'))
                }
            }, trigger: 'change' }
        ]

    }
    return [{ required: property?.required ?? true, message: '请输入参数值', trigger: 'blur' }]
}

const robotFormAction = ref()
const stationListLoading = ref(false)
const POSITION_ACTIONS = ['AGV_MOVE_TO_POINT', 'ARM_MOVE_TO_POINT', 'ARM_MOVE_TO_J']
const hasBindableDevice = computed(() =>
    formData.nodeParams.some(param => param.name === 'deviceId')
    && Boolean(getDeviceKindForAction(props.data.properties.action))
)
const robotActionButtonText = computed(() => {
    if (props.data.properties.action === 'AGV_MOVE_TO_STATION') return '选择机器人并加载站点'
    if (POSITION_ACTIONS.includes(props.data.properties.action)) return '选择机器人并获取位置'
    return '选择机器人设备'
})
const robotDialogTitle = computed(() => {
    if (robotFormAction.value === 'AGV_MOVE_TO_STATION') return '选择AGV并加载站点'
    if (POSITION_ACTIONS.includes(robotFormAction.value)) return '选择机器人并读取当前位置'
    return '选择机器人设备'
})

/**
 * 处理 submitRobotForm 对应的数据或交互；作用范围仅限当前组件或模块。
 */
const submitRobotForm = () => {
    if (robotFormAction.value === 'AGV_MOVE_TO_POINT') {
        robotFormRef.value.validate(async (valid) => {
            if (valid) {
                try {
                    const response = await getAgvStatus({ deviceId: robotForm.deviceId, robotId: robotForm.robotId })
                    if (response && response.data) {
                        dialogVisible.value = false
                        const { x, y, theta } = response.data.pose
                        const testMap = {
                            x: x,
                            y: y,
                            theta: theta,
                            deviceId: robotForm.deviceId
                        }
                        // 将获取到的位置信息设置到 formData 中
                        formData.nodeParams.forEach(param => {
                            if (testMap.hasOwnProperty(param.name)) {
                                param.input = testMap[param.name]
                            }
                        })
                    }
                } catch (error) {
                    console.error('获取位置信息失败:', error)
                }
            }
        })
    } else if (robotFormAction.value === 'AGV_MOVE_TO_STATION') {
        robotFormRef.value.validate(async (valid) => {
            if (valid) {
                stationListLoading.value = true
                try {
                    const response = await getAgvStations({
                        deviceId: robotForm.deviceId,
                        robotId: robotForm.robotId
                    })
                    const stationOptions = toStationOptions(response?.data)
                    if (stationOptions.length === 0) {
                        throw new Error('AGV未返回可用站点')
                    }
                    const stationParam = formData.nodeParams.find(param => param.name === 'stationId')
                    const deviceParam = formData.nodeParams.find(param => param.name === 'deviceId')
                    if (stationParam) {
                        stationParam.selectOptions = stationOptions
                        stationParam.stationDeviceId = robotForm.deviceId
                        if (!stationOptions.some(item => item.value === stationParam.input)) {
                            stationParam.input = ''
                        }
                    }
                    if (deviceParam) deviceParam.input = robotForm.deviceId
                    dialogVisible.value = false
                    ElMessage.success(`已加载${stationOptions.length}个AGV站点`)
                } catch (error) {
                    console.error('加载AGV站点列表失败:', error)
                    ElMessage.error(error?.message || '加载AGV站点列表失败')
                } finally {
                    stationListLoading.value = false
                }
            }
        })
    } else if (robotFormAction.value === 'ARM_MOVE_TO_POINT') {
        robotFormRef.value.validate(async (valid) => {
            if (valid) {
                try {
                    const response = await getArmStatus({ deviceId: robotForm.deviceId, robotId: robotForm.robotId })
                    if (response && response.data) {
                        dialogVisible.value = false
                        const { x, y, z, rx, ry, rz } = response.data
                        const testMap = {
                            x: x,
                            y: y,
                            z: z,
                            rx: rx,
                            ry: ry,
                            rz: rz,
                            deviceId: robotForm.deviceId
                        }
                        // 将获取到的位置信息设置到 formData 中
                        formData.nodeParams.forEach(param => {
                            if (testMap.hasOwnProperty(param.name)) {
                                param.input = testMap[param.name]
                            }
                        })
                    }
                } catch (error) {
                    console.error('获取位置信息失败:', error)
                }
            }
        })
    } else if (robotFormAction.value === 'ARM_MOVE_TO_J') {
        robotFormRef.value.validate(async (valid) => {
            if (valid) {
                try {
                    const response = await getArmJointState({
                        deviceId: robotForm.deviceId,
                        robotId: robotForm.robotId
                    })
                    if (response && response.data) {
                        const target = response.data.position || response.data.positions
                        if (!Array.isArray(target) || target.length === 0) {
                            throw new Error('机械臂未返回有效的关节位置')
                        }
                        dialogVisible.value = false
                        const targetParam = formData.nodeParams.find(param => param.name === 'target')
                        const deviceParam = formData.nodeParams.find(param => param.name === 'deviceId')
                        if (targetParam) targetParam.input = JSON.stringify(target)
                        if (deviceParam) deviceParam.input = robotForm.deviceId
                    }
                } catch (error) {
                    console.error('获取机械臂关节位置失败:', error)
                }
            }
        })
    } else {
        robotFormRef.value.validate((valid) => {
            if (!valid) return
            const deviceParam = formData.nodeParams.find(param => param.name === 'deviceId')
            if (deviceParam) {
                deviceParam.input = robotForm.deviceId
                deviceParam.componentType = 'select'
                deviceParam.selectOptions = [...deviceOptions.value]
                deviceParam.deviceLocked = deviceOptions.value.length === 1
            }
            dialogVisible.value = false
            ElMessage.success('机器人设备已匹配')
        })
    }
}

/**
 * 处理 toStationOptions 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} stations 站点集合
 */
const toStationOptions = (stations) => (Array.isArray(stations) ? stations : [])
    .filter(station => station?.id !== undefined && station?.id !== null && String(station.id).trim())
    .map(station => {
        const id = String(station.id).trim()
        const description = station.description ? String(station.description).trim() : ''
        return {
            value: id,
            label: description ? `${id} - ${description}` : id
        }
    })

/**
 * 打开 openRobotForm 对应的数据或交互；作用范围仅限当前组件或模块。
 * @param {*} action 节点动作类型
 */
const openRobotForm = async (action) => {
    dialogVisible.value = true
    robotFormAction.value = action
    const currentDeviceId = formData.nodeParams.find(param => param.name === 'deviceId')?.input || ''
    robotForm.deviceId = currentDeviceId
    await loadRobotOptions()
    if (robotForm.robotId) await loadRobotDevices(robotForm.robotId)
}

defineExpose({ validateAndSave })
</script>

<style lang="scss" scoped>
.form__container {
    margin: 12px 0;

    :deep(.el-row) {
        align-items: end;

        .el-form-item {
            margin-right: 12px;
        }

        .param-name {
            width: 160px;
        }

        .param-type {
            width: 80px;
        }

        .param-value {
            width: 160px;
        }

    }



    .sub-properties {
        margin-left: 10px;

        .zw {
            margin-left: 15px;

            &::before {
                content: "";
                position: absolute;
                left: 0;
                top: -4px;
                width: 10px;
                border-left: 1px solid gray;
                border-bottom: 1px solid gray;
                border-bottom-left-radius: 4px;
                background-color: transparent;
                height: 24px;
            }
        }
    }

    .gSon-properties {
        margin-left: 10px;

        .zw {
            margin-left: 15px;

            &::before {
                content: "";
                position: absolute;
                left: 0;
                top: -4px;
                width: 10px;
                border-left: 1px solid gray;
                border-bottom: 1px solid gray;
                border-bottom-left-radius: 4px;
                background-color: transparent;
                height: 24px;
            }
        }
    }

    .deleteBtn {
        margin-bottom: 22px;
        cursor: pointer;
    }
}

.robot-device-tip {
    margin: -4px 0 18px;
    padding: 10px 12px;
    border-left: 3px solid var(--el-color-primary);
    background: var(--el-fill-color-light);
    color: var(--el-text-color-regular);
    font-size: 13px;
    line-height: 20px;
}

.device-auto-hint {
    color: var(--el-color-success);
    font-size: 12px;
}
</style>
