<template>
  <Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
    <el-form
      ref="formRef"
      :model="formData"
      :rules="formRules"
      label-width="auto"
      v-loading="formLoading"
      class="energy-device-dialog-form"
    >
      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogCodeLabel')" prop="code">
        <el-input
          v-model="formData.code"
          :placeholder="t('EnergyManagement.EnergyDevice.dialogCodePlaceholder')"
          :disabled="formType === 'update'"
        />
      </el-form-item>

      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogNameLabel')" prop="name">
        <el-input
          v-model="formData.name"
          :placeholder="t('EnergyManagement.EnergyDevice.dialogNamePlaceholder')"
        />
      </el-form-item>

      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogEnergyTypeLabel')" prop="deviceTypeId">
        <el-select
          v-model="formData.deviceTypeId"
          @change="handleDeviceTypeChange"
          :placeholder="t('EnergyManagement.EnergyDevice.dialogEnergyTypePlaceholder')"
          clearable
          filterable
          class="!w-full"
        >
          <el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
        </el-select>
      </el-form-item>

      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogOrgLabel')" prop="orgId">
        <el-tree-select
          v-model="formData.orgId"
          :data="orgSelectTree"
          :props="orgTreeSelectProps"
          filterable
          check-strictly
          clearable
          class="!w-full"
          :placeholder="t('EnergyManagement.EnergyDevice.dialogOrgTreePlaceholder')"
          :loading="analysisLoading"
          :render-after-expand="false"
          @change="handleOrgChange"
        />
      </el-form-item>

      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogRulesLabel')" prop="operationRulesVOList">
        <div class="w-full flex flex-col gap-8px">
          <div
v-for="(rule, index) in formData.operationRulesVOList" :key="index"
            class="w-full flex items-center gap-8px">
            <el-tree-select
              v-model="rule.pointValue"
              :data="equipmentTree"
              :props="treeSelectProps"
              filterable
              clearable
              class="!w-full"
              :placeholder="t('EnergyManagement.EnergyDevice.dialogRulesPointPlaceholder')"
              :loading="devicePointLoading"
              @change="(val) => handlePointSelected(index, val)"
            />

            <template v-if="index < formData.operationRulesVOList.length - 1">
              <el-select
                v-model="formData.operationRulesVOList[index].operator"
                :placeholder="t('EnergyManagement.EnergyDevice.dialogOperatorPlaceholder')"
                class="!w-110px"
                clearable
              >
                <el-option v-for="op in operatorOptions" :key="op" :label="op" :value="op" />
              </el-select>
            </template>
            <template v-else>
              <div class="flex items-center gap-4px">
                <el-button link type="primary" @click="addRule">
                  <Icon icon="ep:plus" />
                </el-button>
                <el-button link type="danger" :disabled="formData.operationRulesVOList.length <= 1" @click="removeRule">
                  <el-icon>
                    <Remove />
                  </el-icon>
                </el-button>
              </div>
            </template>
          </div>
          <div class="text-12px text-[#909399] leading-20px">按“计算规则”添加顺序从上到下依次计算，不按乘除优先。</div>
        </div>
      </el-form-item>

      <el-form-item :label="t('EnergyManagement.EnergyDevice.dialogIsEnableLabel')" prop="isEnable">
        <el-switch v-model="formData.isEnable" />
      </el-form-item>
    </el-form>
    <!-- <el-form-item label="信息资料" prop="info">
      <Editor v-model="formData.info" height="250px" />
    </el-form-item> -->
    <template #footer>
      <el-button @click="submitForm" type="primary" :disabled="formLoading">
        {{ t('EnergyManagement.EnergyDevice.dialogSubmitButtonText') }}
      </el-button>
      <el-button @click="dialogVisible = false">
        {{ t('EnergyManagement.EnergyDevice.dialogCancelButtonText') }}
      </el-button>
    </template>
  </Dialog>
</template>
<script setup lang="ts">
import { EnergyDeviceApi, EnergyDeviceVO } from '@/api/mes/energydevice'
import { EnergyTypeApi, EnergyTypeVO } from "@/api/mes/energytype";
import { OrganizationApi, DeviceParameterAnalysisNodeVO } from '@/api/mes/organization'
import { DeviceApi } from '@/api/iot/device'
import { Remove } from '@element-plus/icons-vue'
import { handleTree } from '@/utils/tree'

/** 能源设备 表单 */
defineOptions({ name: 'EnergyDeviceForm' })

const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗

const typeList = ref<EnergyTypeVO[]>([]) // 列表
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中：1）修改时的数据加载；2）提交的按钮禁用
const formType = ref('') // 表单的类型：create - 新增；update - 修改
const formData = ref({
  id: undefined,
  name: undefined,
  code: undefined,
  deviceTypeId: undefined,
  deviceTypeName: undefined,
  info: undefined,
  checkCron: undefined,
  lastCheckTime: undefined,
  lastCheckValue: undefined,
  unitName: undefined,
  isEnable: undefined,
  orgId: undefined,
  orgName: undefined,
  rules: undefined,
  operationRulesVOList: [{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }],
})
const formRules = reactive({
	name: [{ required: true, message: t('EnergyManagement.EnergyDevice.validatorNameRequired'), trigger: 'blur' }],
	code: [{ required: true, message: t('EnergyManagement.EnergyDevice.validatorCodeRequired'), trigger: 'blur' }],
	orgId: [{ required: true, message: t('EnergyManagement.EnergyDevice.validatorOrgRequired'), trigger: 'change' }],
	operationRulesVOList: [{ required: true, validator: (_rule, _value, callback) => validateRules(callback), trigger: 'change' }],
	isEnable: [{ required: true, message: t('EnergyManagement.EnergyDevice.validatorIsEnableRequired'), trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref

const analysisLoading = ref(false)
type OrgAnalysisNode = DeviceParameterAnalysisNodeVO & { children?: OrgAnalysisNode[] }

const analysisList = ref<OrgAnalysisNode[]>([])

const analysisTree = computed(() => {
  const list = analysisList.value ?? []
  if (!list.length) return [] as OrgAnalysisNode[]

  const hasChildren = list.some((n) => Array.isArray((n as any).children) && (n as any).children.length)
  if (hasChildren) {
    return list.map((n) => ({ ...n })) as OrgAnalysisNode[]
  }

  const hasParentId = list.some((n) => (n as any).parentId !== undefined && (n as any).parentId !== null)
  if (!hasParentId) {
    return list.map((n) => ({ ...n })) as OrgAnalysisNode[]
  }

  const cloned = list.map((n) => ({ ...n })) as OrgAnalysisNode[]
  return handleTree(cloned as any[], 'id', 'parentId', 'children') as OrgAnalysisNode[]
})

type OrgSelectNode = {
  id: number | string
  name: string
  children?: OrgSelectNode[]
}

const orgSelectTree = computed(() => {
  const tree = analysisTree.value ?? []
  if (!tree.length) return [] as OrgSelectNode[]

  const pruneAndAttach = (nodes: OrgAnalysisNode[]): OrgSelectNode[] => {
    const kept: OrgSelectNode[] = []
    nodes.forEach((node) => {
      const childNodes = Array.isArray(node.children) ? pruneAndAttach(node.children) : []
      kept.push({ id: node.id, name: node.name, children: childNodes.length ? childNodes : undefined })
    })
    return kept
  }

  return pruneAndAttach(tree)
})

const orgTreeSelectProps = {
  label: 'name',
  children: 'children',
  value: 'id'
}

const treeSelectProps = {
  label: 'name',
  children: 'children',
  disabled: 'disabled',
  value: 'id'
}

const operatorOptions = ['+', '-', '*', '/']

const isSameId = (a: any, b: any) => {
  return String(a ?? '') === String(b ?? '')
}

const findOrgNode = (nodes: OrgAnalysisNode[], id: any): OrgAnalysisNode | undefined => {
  for (const node of nodes) {
    if (isSameId(node.id, id)) return node
    const children = Array.isArray(node.children) ? node.children : []
    if (children.length) {
      const found = findOrgNode(children, id)
      if (found) return found
    }
  }
  return undefined
}

const devicePointLoading = ref(false)
const devicePointTree = ref<any[]>([])

const loadDevicePointTree = async () => {
  if (devicePointTree.value.length > 0) {
    return
  }
  devicePointLoading.value = true
  try {
    const res: any = await DeviceApi.devicePointList()
    const list = Array.isArray(res) ? res : Array.isArray(res?.data) ? res.data : []

    const normalizeToString = (v: any) => String(v ?? '')
    const isNonEmptyArray = (v: any) => Array.isArray(v) && v.length > 0

    const deviceGroups = new Map<string, { deviceId: string; deviceName: string; points: any[] }>()

    const pushPoint = (deviceId: any, deviceName: any, pointId: any, pointName: any, dataType?: any) => {
      const dId = normalizeToString(deviceId)
      if (!dId) return
      const group = deviceGroups.get(dId) ?? { deviceId: dId, deviceName: String(deviceName ?? ''), points: [] }
      if (!group.deviceName) group.deviceName = String(deviceName ?? '')
      const pId = normalizeToString(pointId)
      if (!pId) {
        deviceGroups.set(dId, group)
        return
      }
      group.points.push({ id: `${dId}:${pId}`, name: `${group.deviceName}: ${String(pointName ?? '')}`.trim(), dataType })
      deviceGroups.set(dId, group)
    }

    if (isNonEmptyArray(list) && ((list[0] as any)?.deviceId !== undefined || (list[0] as any)?.deviceName !== undefined)) {
      ; (list as any[]).forEach((row) => {
        const deviceId = row?.deviceId ?? row?.devId ?? row?.device_id
        const deviceName = row?.deviceName ?? row?.devName ?? row?.device_name
        const points =
          row?.contactModelDOList ??
          row?.points ??
          row?.pointList ??
          row?.devicePoints ??
          row?.devicePointList ??
          row?.parameters
        if (Array.isArray(points)) {
          points.forEach((p) => {
            const pointId = p?.pointId ?? p?.id
            const pointName = p?.attributeName ?? p?.pointName ?? p?.name
            pushPoint(deviceId, deviceName, pointId, pointName, (p as any)?.dataType)
          })
        } else {
          const pointId = row?.pointId ?? row?.attributeId ?? row?.devicePointId
          const pointName = row?.attributeName ?? row?.pointName ?? row?.attributeName
          pushPoint(deviceId, deviceName, pointId, pointName)
        }
      })
    } else if (isNonEmptyArray(list)) {
      ; (list as any[]).forEach((row) => {
        const deviceId = row?.deviceId ?? row?.devId
        const deviceName = row?.deviceName ?? row?.devName
        const pointId = row?.pointId ?? row?.id
        const pointName = row?.attributeName ?? row?.pointName ?? row?.name
        pushPoint(deviceId, deviceName, pointId, pointName, (row as any)?.dataType)
      })
    }

    devicePointTree.value = Array.from(deviceGroups.values())
      .map((g) => {
        const children = (g.points ?? []).filter((p) => p?.id)
        if (!children.length) return null
        return { id: `device:${g.deviceId}`, name: g.deviceName, disabled: true, children }
      })
      .filter(Boolean) as any[]
  } finally {
    devicePointLoading.value = false
  }
}

const equipmentTree = computed(() => {
  return devicePointTree.value
})

const findPointDataType = (deviceId: any, pointId: any): string | undefined => {
  const dId = String(deviceId ?? '')
  const pId = String(pointId ?? '')
  if (!dId || !pId) return undefined
  const key = `${dId}:${pId}`
  const groups = devicePointTree.value ?? []
  for (const g of groups) {
    const children = Array.isArray((g as any).children) ? (g as any).children : []
    for (const c of children) {
      if (String((c as any).id ?? '') === key) {
        return (c as any).dataType as string | undefined
      }
    }
  }
  return undefined
}

/** 打开弹窗 */
const open = async (type: string, id?: number) => {
  dialogVisible.value = true
  dialogTitle.value = t('action.' + type)
  formType.value = type
  resetForm()

  await loadAnalysis()
  await loadDevicePointTree()
  // 修改时，设置数据
  if (id) {
    formLoading.value = true
    try {
      formData.value = await EnergyDeviceApi.getEnergyDevice(id)
      if (!Array.isArray((formData.value as any).operationRulesVOList)) {
        ; (formData.value as any).operationRulesVOList = [
          { deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }
        ]
      }
      hydratePointValues()
    } finally {
      formLoading.value = false
    }
  }
}
defineExpose({ open }) // 提供 open 方法，用于打开弹窗

/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件，用于操作成功后的回调
const submitForm = async () => {
  // 校验表单
  await formRef.value.validate()
  // 提交请求
  formLoading.value = true
  try {
    const base = formData.value as unknown as EnergyDeviceVO
    const payload: any = {
      ...(base as any),
      deviceTypeName: typeList.value.find(item => item.id === base.deviceTypeId)?.name,
      operationRulesVOList: buildOperationRulesPayload()
    }

    const org = findOrgNode(analysisTree.value ?? [], base.orgId)
    if (org) {
      payload.orgName = org.name
    }

    if (formType.value === 'create') {
      await EnergyDeviceApi.createEnergyDevice(payload)
      message.success(t('common.createSuccess'))
    } else {
      await EnergyDeviceApi.updateEnergyDevice(payload)
      message.success(t('common.updateSuccess'))
    }
    dialogVisible.value = false
    // 发送操作成功的事件
    emit('success')
  } finally {
    formLoading.value = false
  }
}

/** 重置表单 */
const resetForm = () => {
  formData.value = {
    id: undefined,
    name: undefined,
    code: undefined,
    deviceTypeId: undefined,
    deviceTypeName: undefined,
    info: undefined,
    checkCron: undefined,
    lastCheckTime: undefined,
    lastCheckValue: undefined,
    unitName: undefined,
    isEnable: true,
    orgId: undefined,
    orgName: undefined,
    rules: undefined,
    operationRulesVOList: [{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }]
  }
  formRef.value?.resetFields()
}

/** 初始化 **/
onMounted(async () => {
  typeList.value = await EnergyTypeApi.getEnergyTypeList()
})

const loadAnalysis = async () => {
  if (analysisList.value.length > 0) {
    return
  }
  analysisLoading.value = true
  try {
    const res: any = await OrganizationApi.deviceParameterAnalysis({ showDevices: 2 })
    const list = Array.isArray(res) ? res : Array.isArray(res?.data) ? res.data : []
    analysisList.value = list as OrgAnalysisNode[]
  } finally {
    analysisLoading.value = false
  }
}

const handleDeviceTypeChange = () => {
  formData.value.unitName = typeList.value.find(item => item.id === formData.value.deviceTypeId)?.unit
}

const handleOrgChange = () => {
  const org = findOrgNode(analysisTree.value ?? [], formData.value.orgId)
  formData.value.orgName = org?.name
}

const handlePointSelected = (index: number, val: any) => {
  if (!val || typeof val !== 'string' || !val.includes(':')) {
    formData.value.operationRulesVOList[index].deviceId = undefined
    formData.value.operationRulesVOList[index].pointId = undefined
    return
  }
  const [deviceIdStr, pointIdStr] = val.split(':')
  const deviceId = Number(deviceIdStr)
  const pointId = Number(pointIdStr)
  formData.value.operationRulesVOList[index].deviceId = Number.isNaN(deviceId) ? undefined : deviceId
  formData.value.operationRulesVOList[index].pointId = Number.isNaN(pointId) ? undefined : pointId
}

const addRule = () => {
  formData.value.operationRulesVOList.push({
    deviceId: undefined,
    pointId: undefined,
    operator: undefined,
    pointValue: undefined
  })
}

const removeRule = () => {
  if (formData.value.operationRulesVOList.length <= 1) {
    return
  }
  formData.value.operationRulesVOList.pop()
}

const buildOperationRulesPayload = () => {
  const list = (formData.value.operationRulesVOList ?? []).map((r, idx) => {
    const item: any = {
      deviceId: r.deviceId,
      pointId: r.pointId
    }
    if (r.operator) {
      item.operator = r.operator
    }
    return item
  })
  return list
}

const hydratePointValues = () => {
  const list = (formData.value as any).operationRulesVOList as any[]
  if (!Array.isArray(list)) {
    return
  }
  list.forEach((r) => {
    if (r?.deviceId && r?.pointId) {
      r.pointValue = `${r.deviceId}:${r.pointId}`
    }
  })
}

const validateRules = (callback: any) => {
  const list = formData.value.operationRulesVOList ?? []
  if (!list.length) {
		callback(new Error(t('EnergyManagement.EnergyDevice.validatorRulesRequired')))
    return
  }
  for (let i = 0; i < list.length; i++) {
    const r = list[i] as any
    if (!r?.deviceId || !r?.pointId) {
			callback(new Error(t('EnergyManagement.EnergyDevice.validatorRulesPointRequired')))
      return
    }
    if (i > 0 && !list[i - 1]?.operator) {
			callback(new Error(t('EnergyManagement.EnergyDevice.validatorRulesOperatorRequired')))
      return
    }
    const dt = findPointDataType(r.deviceId, r.pointId)
    const dtText = String(dt ?? '').toLowerCase()
    if (dtText === 'bool' || dtText === 'boolean') {
      message.warning('布尔类型点位暂不支持用于计算规则，请重新选择')
      callback(new Error('布尔类型点位暂不支持用于计算规则，请重新选择'))
      return
    }
  }
  callback()
}
</script>

<style scoped>
.energy-device-dialog-form :deep(.el-form-item__label) {
	min-width: 100px;
}
</style>
