<template>
  <div class="node__container" :class="props.model.id">
    <NodeState :state="nodeOperatingStatus">
      <template #input>
        <JsonViewer :value="inputJsonData" copyable boxed sort theme="light" />
      </template>
      <template #output>
        <JsonViewer
          v-if="props.properties.outputType === 'json'"
          :value="outputJsonData"
          copyable
          boxed
          sort
          theme="light"
        />
        <el-image
          v-if="props.properties.outputType === 'img'"
          v-for="item in outputJsonData.imageUrl"
          style="width: 60px; height: 60px"
          :src="item"
          :preview-src-list="outputJsonData.imageUrl"
          :preview-teleported="true"
          show-progress
          fit="fill"
        />
        <IPlayer
          v-if="props.properties.outputType === 'video'"
          v-for="item in outputJsonData.videoUrl"
          :videoUrl="item"
        />
      </template>
    </NodeState>
    <NodeTitle
      :icon="props.properties.icon"
      :nodeId="props.model.id"
      :nodeProperties="props.properties"
      :nodeType="props.properties.nodeType || 'NONE'"
      :nodeName="props.properties.name"
      :nodeDesc="props.properties.desc"
      :zoom-state="nodeZoom"
      @zoom="zoom"
      @setNodeName="setNodeName"
    />
    <div class="input__container" v-show="nodeZoom">
      <div class="title">
        <div class="left">
          <div class="tag"></div>
          <div class="text">输入</div>
        </div>
        <div class="right" v-if="properties?.canAddFormItem">
          <el-button
            :disabled="flowStore.disableForm"
            @click="addFormItem"
            class="addFormItem"
          >
            <el-icon :size="20"><Plus /></el-icon>
          </el-button>
        </div>
      </div>
      <div class="form__container">
        <el-form
          :inline="true"
          :model="formData"
          :rules="rules"
          ref="dynamicForm"
          label-position="top"
          label-width="auto"
          :disabled="flowStore.disableForm"
        >
          <div v-for="(property, index) in formData.nodeParams" :key="index">
            <el-row v-if="property.name !== 'code'">
              <el-form-item
                :label="index === 0 ? '参数名' : ''"
                :prop="`nodeParams.${index}.name`"
                :rules="[
                  { required: true, message: '请输入参数名', trigger: 'blur' },
                ]"
              >
                <el-input
                  :disabled="property?.disabled || false"
                  v-model="property.name"
                  @keydown="handleInputKeydown"
                  placeholder="请输入"
                  clearable
                />
              </el-form-item>
              <el-form-item
                :label="index === 0 ? '参数值' : ''"
                :prop="`nodeParams.${index}.type`"
              >
                <el-select
                  v-model="property.type"
                  @change="handleTypeChange(index)"
                >
                  <el-option label="引用" value="quote" />
                  <el-option label="输入" value="input" />
                </el-select>
                <el-form-item
                  v-if="property.type === 'input'"
                  :rules="[
                    {
                      required: property?.required ?? true,
                      message: '请输入参数值',
                      trigger: 'blur',
                    },
                  ]"
                  :prop="`nodeParams.${index}.input`"
                >
                  <el-input-number
                    v-if="property.componentType === 'number'"
                    v-model="property.input"
                    :min="0"
                    :controls="false"
                    :step-strictly="true"
                    placeholder="请输入"
                    clearable
                    @keydown="handleInputKeydown"
                  />
                  <el-select
                    v-model="property.input"
                    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
                    @keydown="handleInputKeydown"
                    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)"
                  />
                </el-form-item>
              </el-form-item>
            </el-row>
          </div>
        </el-form>
      </div>
      <div class="code__container">
        <div ref="codeRef" style="width: 100%; height: 100%"></div>
      </div>
    </div>
    <div class="output__container" v-show="nodeZoom">
      <div v-if="props.properties?.outputParams?.length > 0">
        <div class="title">
          <div class="left">
            <div class="tag"></div>
            <div class="text">输出</div>
          </div>
          <div class="right">
            <el-button
              :disabled="flowStore.disableForm"
              @click="addOutputFormItem"
              class="addFormItem"
            >
              <el-icon :size="20"><Plus /></el-icon>
            </el-button>
          </div>
        </div>

        <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="deleteTopLevelItem"
            />
          </el-form>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted, onUnmounted, nextTick } from "vue";
import { getInput, initNodeZoom } from "@/utils/flow";
import { useFlowStore } from "@/store/modules/flow";
import { Plus } from "@element-plus/icons-vue";
import NodeTitle from "../../components/NodeTitle.vue";
import NodeState from "../../components/NodeState.vue";
import "vue3-json-viewer/dist/index.css";
import { emitter } from "@/utils/eventBus";
import IPlayer from "@/views/device/register/components/LogicFlow/IPlayer/index.vue";
import FormItemRecursive from "../common/FormItemRecursive.vue";
import * as monaco from 'monaco-editor';
import { ElMessage } from "element-plus";

const props = defineProps({
  model: Object,
  properties: Object,
});

const emits = defineEmits(["contentChange"]);

const codeRef = ref()
let editorInstance

const flowStore = useFlowStore();

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

const rules = reactive({});

const quoteOptions = ref([]);
const handleTypeChange = (index) => {
  if (formData.nodeParams[index].type === "input") {
    formData.nodeParams[index].quote = "";
  } else {
    formData.nodeParams[index].input = "";
    const option = getInput(props.model.id);
    if (option) {
      quoteOptions.value = option;
    }
  }
};

const cascaderRefs = ref([]);
const outputRules = reactive({});
const cascaderChange = (value, index) => {
  const selectedOptions = cascaderRefs.value[index].getCheckedNodes(true);
  formData.nodeParams[index].quote = value;
  formData.nodeParams[index].quoteType = selectedOptions[0].data.type;
};

const addOutputFormItem = () => {
  formData.outputParams.push({
    name: "",
    type: "",
    desc: "",
    children: [],
  });
};

const dynamicForm = ref();
const outputFormRef = ref();
const setNodeProperties = async () => {
  try {
    const result = await dynamicForm.value.validate();
    let flag = true;
    if (outputFormRef.value) {
      flag = await outputFormRef.value.validate();
    }
    const errorLength = validateCode()
    if (errorLength > 0) {
      ElMessage.error("代码节点内JS代码错误")
      return
    }
    if (result && flag) {
      const data = toRaw(formData);

      const targetObj = data.nodeParams.find(item => item.name === 'code');

      if (targetObj) {
        // 存在：修改该对象的 input 属性
        targetObj.input = editorInstance.getValue();
      } else {
        data.nodeParams.push(
          { name: "code", type: "input", input: editorInstance.getValue() }
        )
      }
      
      const properties = lf.getProperties(props.model.id);
      lf.setProperties(props.model.id, {
        ...properties,
        ...data,
        zoom: nodeZoom.value
      });
      emits("contentChange");
    }
  } catch {
    console.log(12323)
    // dynamicForm.value.clearValidate();
  }
};

const setNodeName = (name) => {
  const properties = lf.getProperties(props.model.id);
  lf.setProperties(props.model.id, {
    ...properties,
    name
  });
  emits("contentChange");
}

const nodeOperatingStatus = ref("NORMAL");
const inputJsonData = ref({});
const outputJsonData = ref({});

const visibleChange = (value, index, quote) => {
  if (value) {
    const option = getInput(props.model.id);
    if (option) {
      quoteOptions.value = option;
      const currentValue = [...quote];
      // 强制级联选择器重新处理选中值与选项的匹配
      if (cascaderRefs.value[index]) {
        // 等待DOM更新后再设置值，确保新options已生效
        setTimeout(() => {
          // 如果当前有选中值，重新设置一次以触发重新匹配
          if (currentValue.length) {
            formData.nodeParams[index].quote = [];
            // 确保响应式更新
            setTimeout(() => {
              formData.nodeParams[index].quote = currentValue;
            }, 0);
          }
          // 手动触发级联选择器的重新渲染
          // cascaderRefs.value[index].updatePopper();
        }, 0);
      }
    }
  }
};

const validateForm = async () => {
  try {
    const result = await dynamicForm.value.validate();
    if (result) {
      return { valid: true, message: "验证通过" };
    } else {
      return { valid: false, message: "验证失败" };
    }
  } catch {
    return { valid: false, message: "验证失败" };
  }
};

const addFormItem = () => {
  formData.nodeParams.push({
    name: "",
    type: "input",
    desc: "",
    required: false,
    children: [],
  });
  emits("contentChange");
};

// 删除顶层表单项
const deleteTopLevelItem = (fullPath) => {
  // 从顶层数据开始查找
  let currentLevel = formData.outputParams;

  // 遍历路径（除最后一个索引，因为最后一个是要删除的项）
  for (let i = 0; i < fullPath.length - 1; i++) {
    const index = fullPath[i];
    // 进入下一层级
    currentLevel = currentLevel[index].children;
  }

  // 最后一个索引是当前层级要删除的项
  const lastIndex = fullPath[fullPath.length - 1];
  currentLevel.splice(lastIndex, 1);
  emits("contentChange");
};

const nodeZoom = ref(props?.properties?.zoom ?? true)
const zoom = (flag) => {
  nodeZoom.value = flag
  initNodeZoom(props.model.id, nodeZoom.value, '.node__box')
}

watch(
  () => props.properties,
  () => {
    if (props.properties.nodeParams && props.properties.nodeParams.length > 0) {

      // .filter(item => item.name !== 'code')
      formData.nodeParams = props.properties.nodeParams;
      const codeParams = props.properties.nodeParams.find(item => item.name === 'code')
      if (codeParams) {
        nextTick(() => {
          if (editorInstance) {
            setValue(codeParams.input)
          }
        })
      }
      const option = getInput(props.model.id);
      if (option) {
        quoteOptions.value = option;
      }

      nextTick(() => {
        initNodeZoom(props.model.id, props?.properties?.zoom ?? true, '.node__box', true)
      })
    }
    if (
      props.properties.outputParams &&
      props.properties.outputParams.length > 0
    ) {
      formData.outputParams = props.properties.outputParams;
    }
  },
  {
    immediate: true,
    deep: true,
  }
);

const handleInputKeydown = (e) => {
  // 阻止事件冒泡到 Logic Flow 节点，避免被其事件拦截
  e.stopPropagation();
  // 可选：明确放行 Ctrl+V（增强兼容性）
  if ((e.ctrlKey || e.metaKey) && e.key === "v") {
    e.returnValue = true; // 允许默认粘贴行为
  }
};

const initEditor = () => {
  editorInstance = monaco.editor.create(codeRef.value, {
    value: [`
    // 方法定义不能修改
    function handler(params) {
        // 返回值是一个可序列化成 json 的 dict 或 object
        const ret={
            result: {
                type: 2,
                message: params.input
            }
            
        }
        return ret
    }`].join('\n'),
    language: 'javascript',
    fontSize: 14,
    formatOnPaste: true,
    formatOnType: true
  });
}

// 设置代码值
const setValue = (value) => {
  if (editorInstance) {
    editorInstance.setValue(value)
  }
}

// 验证代码
const validateCode = () => {
  if (!editorInstance) return

  const code = editorInstance.getValue()
  const errors = []

  try {
    // 1. 基础语法检查
    try {
      new Function(code)
    } catch (syntaxError) {
      errors.push({
        type: 'syntax',
        message: syntaxError.message,
        line: extractLineNumber(syntaxError)
      })
    }
    // 2. 检查运行时错误（通过尝试执行）
    if (errors.length === 0) {
        try {
            const fn = new Function(code);
            fn(); // 尝试执行
        } catch (runtimeError) {
            errors.push({ type: 'runtime', message: runtimeError.message });
        }
    }

    // 设置 Monaco 标记
    const markers = []

    errors.forEach(error => {
      markers.push({
        severity: monaco.MarkerSeverity.Error,
        startLineNumber: error.line || 1,
        startColumn: 1,
        endLineNumber: error.line || 1,
        endColumn: 100,
        message: error.message
      })
    })
    monaco.editor.setModelMarkers(editorInstance.getModel(), 'validation', markers)
  } catch (error) {
    console.error('验证过程中出错:', error)
  }

  return errors.length
}

// 格式化代码
const formatCode = () => {
  if (!editorInstance) return
  try {
    editorInstance.getAction('editor.action.formatDocument').run()

  } catch (error) {
    console.error('格式化失败:', error)
  }
}

// 销毁编辑器
const disposeEditor = () => {
  if (editorInstance) {
    editorInstance.dispose()
    editorInstance = null
  }
}

onMounted(() => {
  emitter.on("changeNodeState", (data) => {
    if (data.nodeId === props.model.id) {
      nodeOperatingStatus.value = data.status;
      let inputData = {};
      let output = {};
      try {
        inputData = JSON.parse(data.paramsIn) || {};
        output = JSON.parse(data.paramsOut) || {};
      } catch (error) {
        inputData = data.paramsIn || {};
        output = data.paramsOut || {};
      }
      inputJsonData.value = inputData;
      outputJsonData.value = output;
      emits("contentChange");
    }
  });

  emitter.on("contentChange", (data) => {
    if (data.id === props.model.id) {
      nodeOperatingStatus.value = "NORMAL";
      inputJsonData.value = {};
      outputJsonData.value = {};
      emits("contentChange");
    }
  });

  initEditor()
})

onUnmounted(() => {
  emitter.off("changeNodeState");
  emitter.off("contentChange");
  disposeEditor()
});

defineExpose({
  validateForm,
  setNodeProperties,
});
</script>

<style lang="scss" scoped>
.node__container {
  width: 100%;
  height: auto;

  .input__container {
    width: 100%;
    background-color: #fafbfc;
    padding: 0 16px;
    border-radius: 8px;
    box-sizing: border-box;

    .title {
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: space-between;

      .left {
        display: flex;
        align-items: center;

        .tag {
          width: 3px;
          height: 16px;
          background: #1664ff;
          border-radius: 0 4px 4px 0;
          margin-right: 12px;
        }

        .text {
          font-size: 16px;
          font-weight: 700;
          color: #0c0d0e;
        }
      }

      .right {
        .addFormItem {
          border: none;
          background: transparent;
          cursor: pointer;
        }
      }
    }

    .code__container {
      display: flex;
      position: relative;
      text-align: initial;
      width: 100%;
      height: 300px;
      margin: 12px 0;
    }

    .form__container {
      margin: 12px 0;

      .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;
          }
        }
      }
    }
  }

  .output__container {
    width: 100%;
    background-color: #fafbfc;
    padding: 0 16px;
    border-radius: 8px;
    box-sizing: border-box;

    .title {
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: space-between;

      .left {
        display: flex;
        align-items: center;

        .tag {
          width: 3px;
          height: 16px;
          background: #1664ff;
          border-radius: 0 4px 4px 0;
          margin-right: 12px;
        }

        .text {
          font-size: 16px;
          font-weight: 700;
          color: #0c0d0e;
        }
      }

      .right {
        .addFormItem {
          border: none;
          background: transparent;
          cursor: pointer;
        }
      }
    }

    .form__container {
      margin: 12px 0;
    }
  }

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

    .el-input {
      --el-input-width: 148px;
    }

    .el-select {
      --el-select-width: 148px;
    }

    .el-cascader {
      --el-form-inline-content-width: 148px;
    }
  }
}
</style>
