<template>
  <div class="schedule-preview-wrap">
    <div ref="ganttContainerRef" class="schedule-gantt-container" :style="{ height }"></div>
    <div class="schedule-detail-panel">
      <div class="schedule-detail-title">计划信息</div>
      <template v-if="activePreviewDevice">
        <el-descriptions :column="1" border size="small">
          <el-descriptions-item label="设备名称">{{ activePreviewDevice.deviceName }}</el-descriptions-item>
          <el-descriptions-item label="设备ID">{{ activePreviewDevice.deviceId }}</el-descriptions-item>
          <el-descriptions-item label="产能">{{ activePreviewDevice.ratedCapacity ?? '-' }}</el-descriptions-item>
          <el-descriptions-item v-if="'dailyAverageValue' in activePreviewDevice" label="每日报工平均值">{{ activePreviewDevice.dailyAverageValue ?? '-' }}</el-descriptions-item>
          <el-descriptions-item v-if="'dataCollectionCapacity' in activePreviewDevice" label="数据采集产能">{{ activePreviewDevice.dataCollectionCapacity ?? '-' }}</el-descriptions-item>
          <el-descriptions-item label="计划条数">{{ activePreviewDevice.plans?.length ?? 0 }}</el-descriptions-item>
        </el-descriptions>
        <div class="schedule-plan-list-title">计划明细</div>
        <div class="schedule-plan-list">
          <div
            v-for="(plan, index) in activePreviewTask ? [activePreviewTask] : (activePreviewDevice?.plans ?? [])"
            :key="`${activePreviewDevice?.deviceId}-${plan.taskDetailId}-${index}`"
            :class="['schedule-plan-item', { 'schedule-plan-item-active': plan.sourceType === 'CURRENT' }]"
          >
            <div class="schedule-plan-item-head">
              <span class="schedule-plan-item-title">{{ plan.productCode ?? '-' }} / {{ plan.productName ?? '-' }}</span>
            </div>
            <div>计划编码：{{ plan.taskCode ?? '-' }}</div>
            <div>计划数量：{{ plan.planNumber ?? '-' }}</div>
            <div>交货日期：{{ plan.deliveryDateStr ?? '-' }}</div>
            <div>开始：{{ plan.planStartTimeStr || '-' }}</div>
            <div>结束：{{ plan.planEndTimeStr || '-' }}</div>
            <div>最晚开工：{{ plan.latestStartTimeStr || '-' }}</div>
          </div>
        </div>
      </template>
      <el-empty v-else description="暂无计划信息" :image-size="80" />
    </div>
  </div>

  <el-dialog v-model="taskAdjustDialogVisible" title="调整任务" width="420px" append-to-body>
    <el-form label-width="110px">
      <el-form-item label="设备">
        <el-select v-model="taskAdjustForm.deviceTaskId" placeholder="请选择设备" class="!w-full">
          <el-option v-for="item in previewDeviceOptions" :key="item.value" :label="item.label" :value="item.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="计划开始日期">
        <el-date-picker
          v-model="taskAdjustForm.startDate"
          type="datetime"
          value-format="YYYY-MM-DD HH:mm:ss"
          placeholder="请选择计划开始日期"
          class="!w-full"
        />
      </el-form-item>
      <el-form-item label="计划结束日期">
        <el-date-picker
          v-model="taskAdjustForm.endDate"
          type="datetime"
          value-format="YYYY-MM-DD HH:mm:ss"
          placeholder="请选择计划结束日期"
          class="!w-full"
        />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="taskAdjustDialogVisible = false">取消</el-button>
      <el-button type="primary" @click="handleTaskAdjustSubmit">确定</el-button>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import dayjs from 'dayjs'
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'

defineOptions({ name: 'ScheduleGanttPanelEditable' })

const props = withDefaults(
  defineProps<{
    scheduleList: any[]
    height?: string
  }>(),
  {
    height: '800px'
  }
)

const message = useMessage()
const ganttContainerRef = ref<HTMLDivElement>()
const activePreviewDevice = ref<any>()
const activePreviewTask = ref<any>()
const tooltipCleanupFns = ref<(() => void)[]>([])
const ganttEventIds = ref<string[]>([])
const ganttSyncing = ref(false)
const taskAdjustDialogVisible = ref(false)
const taskAdjustTaskId = ref<string | number | null>(null)
const taskAdjustForm = reactive({
  deviceTaskId: '',
  startDate: '',
  endDate: ''
})
const taskMoveFromParentMap = ref<Record<string, string | number | undefined>>({})
const taskDragDurationMap = ref<Record<string, number>>({})

const previewScheduleList = computed(() => (Array.isArray(props.scheduleList) ? props.scheduleList : []))
const previewDeviceOptions = computed(() =>
  previewScheduleList.value.map((device: any) => ({
    label: device?.deviceName ?? '-',
    value: `device-${device?.deviceId}`
  }))
)

const hasCurrentPlan = (device: any) =>
  (device?.plans ?? []).some((plan: any) => String(plan.sourceType ?? '').toUpperCase() === 'CURRENT')

const getGanttScheduleList = () =>
  previewScheduleList.value.filter((device: any) => hasCurrentPlan(device))

const getGlobalDateRange = (scheduleList: any[]) => {
  const allPlans = scheduleList.flatMap((item: any) => item?.plans ?? [])
  const starts = allPlans.map((item: any) => dayjs(item?.planStartTimeStr).valueOf()).filter((item: number) => Number.isFinite(item))
  const ends = allPlans.map((item: any) => dayjs(item?.planEndTimeStr).valueOf()).filter((item: number) => Number.isFinite(item))
  if (!starts.length || !ends.length) {
    const now = Date.now()
    return {
      start: now,
      end: now + 7 * 24 * 60 * 60 * 1000
    }
  }
  return {
    start: Math.min(...starts),
    end: Math.max(...ends)
  }
}

const formatGanttDate = (value: unknown) => {
  const date = dayjs(value)
  if (!date.isValid()) return undefined
  return date.format('YYYY-MM-DD HH:mm')
}

const getDeviceTaskRangeByChildren = (planTasks: any[]) => {
  const starts = planTasks.map((item: any) => dayjs(item?.start_date)).filter((item: any) => item.isValid())
  const ends = planTasks.map((item: any) => dayjs(item?.end_date)).filter((item: any) => item.isValid())
  if (!starts.length || !ends.length) return undefined
  const earliestStart = starts.reduce((min: any, current: any) => (current.valueOf() < min.valueOf() ? current : min))
  const latestEnd = ends.reduce((max: any, current: any) => (current.valueOf() > max.valueOf() ? current : max))
  const duration = Math.max(latestEnd.endOf('day').diff(earliestStart.startOf('day'), 'day') + 1, 1)
  return {
    start_date: earliestStart.toDate(),
    end_date: latestEnd.toDate(),
    duration
  }
}

const buildPreviewGanttData = (scheduleList: any[]) => {
  const tasks: any[] = []
  const links: any[] = []
  let linkIndex = 1

  scheduleList.forEach((device: any) => {
    const deviceId = `device-${device.deviceId}`
    const plans = (device?.plans ?? []).map((plan: any) => ({
      ...plan,
      _start: dayjs(plan?.planStartTimeStr),
      _end: dayjs(plan?.planEndTimeStr)
    }))
    const validPlans = plans
      .filter((plan: any) => plan._start.isValid() && plan._end.isValid())
      .sort((a: any, b: any) => a._start.valueOf() - b._start.valueOf())
    const firstPlan = validPlans[0]
    const deviceRange = getDeviceTaskRangeByChildren(
      validPlans.map((item: any) => ({
        start_date: item?._start,
        end_date: item?._end
      }))
    )

    tasks.push({
      id: deviceId,
      text: `${device.deviceName ?? '-'}`,
      start_date: formatGanttDate(deviceRange?.start_date ?? firstPlan?._start),
      end_date: formatGanttDate(deviceRange?.end_date ?? firstPlan?._end),
      duration: deviceRange?.duration ?? 1,
      parent: 0,
      progress: 0,
      open: true,
      readonly: true,
      deviceName: device.deviceName ?? '-',
      _deviceData: device
    })

    let previousPlanTaskId: string | null = null
    validPlans.forEach((plan: any, index: number) => {
      const startDate = formatGanttDate(plan.planStartTimeStr)
      const endDate = formatGanttDate(plan.planEndTimeStr)
      const duration = Number(plan.scheduleDays) > 0 ? Number(plan.scheduleDays) : Math.max(plan._end.diff(plan._start, 'day') + 1, 1)
      if (!startDate || !endDate) return
      const isHistory = String(plan.sourceType ?? '').toUpperCase() === 'HISTORY'
      const planTaskId = `plan-${device.deviceId}-${plan.taskDetailId ?? index}-${index}`
      tasks.push({
        id: planTaskId,
        text: `${plan.productCode ?? '-'} / ${plan.productName ?? '-'} / ${plan.taskCode ?? '-'}`,
        start_date: startDate,
        end_date: endDate,
        duration,
        parent: deviceId,
        progress: 0,
        readonly: isHistory,
        _planData: plan,
        _deviceData: device
      })
      if (previousPlanTaskId) {
        links.push({
          id: `link-${linkIndex++}`,
          source: previousPlanTaskId,
          target: planTaskId,
          type: '0'
        })
      }
      previousPlanTaskId = planTaskId
    })
  })

  return { data: tasks, links }
}

const cleanupTaskTooltips = () => {
  const tooltipsExt = (gantt.ext as any)?.tooltips
  const tooltip = tooltipsExt?.tooltip
  if (tooltipsExt?.detach) {
    tooltipsExt.detach('.gantt_task_line')
    tooltipsExt.detach('.gantt_task_content')
  }
  if (tooltip?.hide) {
    tooltip.hide()
  }
  const node = tooltip?.getNode?.()
  if (node) {
    node.style.display = 'none'
    node.style.visibility = 'hidden'
    node.style.opacity = '0'
  }
  tooltipCleanupFns.value.forEach((fn) => fn())
  tooltipCleanupFns.value = []
}

const destroyGantt = () => {
  try {
    cleanupTaskTooltips()
  } catch {}
  try {
    ganttEventIds.value.forEach((eventId) => {
      try {
        gantt.detachEvent(eventId)
      } catch {}
    })
  } catch {}
  ganttEventIds.value = []
  try {
    gantt.clearAll()
  } catch {}
}

const formatGridDateText = (value: unknown) => {
  const date = dayjs(value)
  if (!date.isValid()) return '-'
  return date.format('YYYY-MM-DD HH:mm:ss')
}
const formatTooltipDateTime = (value: unknown) => {
  const date = dayjs(value)
  if (!date.isValid()) return '-'
  return date.format('YYYY-MM-DD HH:mm:ss')
}
const getDeviceTaskSummary = (task: any) => {
  if (!task) {
    return {
      planCount: 0,
      totalPlanNumber: 0,
      earliestStart: '-',
      latestEnd: '-'
    }
  }
  const childTaskIds = gantt.getChildren(task.id)
  const childTasks = (Array.isArray(childTaskIds) ? childTaskIds : [])
    .map((childId: string | number) => gantt.getTask(childId))
    .filter((child: any) => child?._planData)
  if (!childTasks.length) {
    return {
      planCount: 0,
      totalPlanNumber: 0,
      earliestStart: '-',
      latestEnd: '-'
    }
  }
  const starts = childTasks.map((child: any) => dayjs(child.start_date).valueOf()).filter((val: number) => Number.isFinite(val))
  const ends = childTasks.map((child: any) => dayjs(child.end_date).valueOf()).filter((val: number) => Number.isFinite(val))
  const totalPlanNumber = childTasks.reduce((sum: number, child: any) => sum + Number(child?._planData?.planNumber ?? 0), 0)
  return {
    planCount: childTasks.length,
    totalPlanNumber,
    earliestStart: starts.length ? formatTooltipDateTime(Math.min(...starts)) : '-',
    latestEnd: ends.length ? formatTooltipDateTime(Math.max(...ends)) : '-'
  }
}
const buildTaskTooltipHtml = (task: any, start?: Date, end?: Date) => {
  const plan = task?._planData
  if (plan) {
    return `
      <div><b>任务明细</b></div>
      <div>任务单：${plan.taskCode ?? '-'}</div>
      <div>产品：${plan.productCode ?? '-'} / ${plan.productName ?? '-'}</div>
      <div>明细ID：${plan.taskDetailId ?? '-'}</div>
      <div>计划数量：${plan.planNumber ?? '-'}</div>
      <div>开始：${formatTooltipDateTime(start ?? task?.start_date)}</div>
      <div>结束：${formatTooltipDateTime(end ?? task?.end_date)}</div>
      <div>最晚开工：${formatTooltipDateTime(plan.latestStartTimeStr)}</div>
    `
  }
  const device = task?._deviceData
  const summary = getDeviceTaskSummary(task)
  return `
    <div><b>汇总</b></div>
    <div>设备：${device?.deviceName ?? '-'}</div>
    <div>任务明细条数：${summary.planCount}</div>
    <div>计划总数：${summary.totalPlanNumber}</div>
    <div>最早计划开始：${summary.earliestStart}</div>
    <div>最晚计划结束：${summary.latestEnd}</div>
  `
}
const getTaskByTooltipNode = (node: HTMLElement) => {
  const holder = node.closest('[task_id]') || node.closest('[data-task-id]') || node.closest('.gantt_task_line') || node
  const taskId = holder?.getAttribute('task_id') || holder?.getAttribute('data-task-id')
  if (!taskId) return undefined
  try {
    return gantt.getTask(taskId)
  } catch {
    return undefined
  }
}

const initTaskTooltips = () => {
  const tooltipsExt = (gantt.ext as any)?.tooltips
  const tooltip = tooltipsExt?.tooltip
  if (!tooltipsExt || !tooltip || !ganttContainerRef.value) return

  let tooltipHideTimer: ReturnType<typeof setTimeout> | null = null

  const forceHideTooltip = () => {
    if (tooltipHideTimer) {
      clearTimeout(tooltipHideTimer)
      tooltipHideTimer = null
    }
    tooltip.hide()
    const node = tooltip.getNode?.()
    if (node) {
      node.style.display = 'none'
      node.style.visibility = 'hidden'
      node.style.opacity = '0'
    }
  }

  const showTooltip = (event: MouseEvent, html: string) => {
    if (tooltipHideTimer) {
      clearTimeout(tooltipHideTimer)
      tooltipHideTimer = null
    }
    tooltip.setContent(html)
    tooltip.show(event)
    const node = tooltip.getNode?.()
    if (node) {
      node.style.display = 'block'
      node.style.visibility = 'visible'
      node.style.opacity = '1'
      node.style.zIndex = '10000'
      node.style.pointerEvents = 'none'
    }
  }

  tooltipsExt.detach('.gantt_task_line')
  tooltipsExt.detach('.gantt_task_content')
  tooltipsExt.attach({
    selector: '.gantt_task_line,.gantt_task_content',
    onmouseenter: (event: MouseEvent, node: HTMLElement) => {
      const task = getTaskByTooltipNode(node)
      if (!task) return
      showTooltip(event, buildTaskTooltipHtml(task))
    },
    onmousemove: (event: MouseEvent, node: HTMLElement) => {
      const task = getTaskByTooltipNode(node)
      if (!task) return
      showTooltip(event, buildTaskTooltipHtml(task))
    },
    onmouseleave: () => {
      forceHideTooltip()
    }
  })

  const containerEl = ganttContainerRef.value
  const containerMouseLeaveHandler = () => {
    forceHideTooltip()
  }
  containerEl.addEventListener('mouseleave', containerMouseLeaveHandler)

  const documentMouseMoveHandler = (e: MouseEvent) => {
    const target = e.target as HTMLElement
    if (!target) return
    const isOverTask = target.closest('.gantt_task_line') || target.closest('.gantt_task_content')
    if (!isOverTask) {
      if (tooltipHideTimer) clearTimeout(tooltipHideTimer)
      tooltipHideTimer = setTimeout(forceHideTooltip, 150)
    } else {
      if (tooltipHideTimer) {
        clearTimeout(tooltipHideTimer)
        tooltipHideTimer = null
      }
    }
  }
  document.addEventListener('mousemove', documentMouseMoveHandler)

  tooltipCleanupFns.value.push(() => {
    containerEl.removeEventListener('mouseleave', containerMouseLeaveHandler)
    document.removeEventListener('mousemove', documentMouseMoveHandler)
    if (tooltipHideTimer) {
      clearTimeout(tooltipHideTimer)
      tooltipHideTimer = null
    }
  })
}

const syncPlanTimeFromTask = (task: any) => {
  if (!task?._planData) return
  if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return
  const start = dayjs(task.start_date)
  const end = dayjs(task.end_date)
  if (!start.isValid() || !end.isValid()) return
  const duration = Math.max(Number(task.duration) || end.diff(start, 'day') + 1, 1)
  task.duration = duration
  task._planData.planStartTimeStr = start.format('YYYY-MM-DD HH:mm:ss')
  task._planData.planEndTimeStr = end.format('YYYY-MM-DD HH:mm:ss')
  task._planData.scheduleDays = duration
  activePreviewDevice.value = task._deviceData
  if (activePreviewTask.value && String(activePreviewTask.value.taskDetailId) === String(task._planData.taskDetailId)) {
    activePreviewTask.value = task._planData
  }
}

const syncDeviceTaskRangeFromChildren = (task: any) => {
  if (!task?._planData) return
  const deviceTaskId = task.parent
  if (!deviceTaskId) return
  const childTaskIds = gantt.getChildren(deviceTaskId)
  if (!Array.isArray(childTaskIds) || !childTaskIds.length) return
  const childTasks = childTaskIds.map((childId: string | number) => gantt.getTask(childId)).filter((item: any) => item?._planData)
  if (!childTasks.length) return
  const deviceRange = getDeviceTaskRangeByChildren(childTasks)
  if (!deviceRange) return
  const deviceTask = gantt.getTask(deviceTaskId)
  deviceTask.start_date = deviceRange.start_date
  deviceTask.end_date = deviceRange.end_date
  deviceTask.duration = deviceRange.duration
  gantt.updateTask(deviceTaskId)
}

const movePlanDataToDevice = (task: any, targetDeviceTaskId: string | number, sourceDeviceTaskId?: string | number) => {
  if (!task?._planData) return
  const targetDeviceTask = gantt.getTask(targetDeviceTaskId)
  const targetDeviceData = targetDeviceTask?._deviceData
  if (!targetDeviceData) return
  const planIdentity = `${task?._planData?.taskId ?? ''}-${task?._planData?.taskDetailId ?? ''}`
  const sourceDeviceTask = sourceDeviceTaskId ? gantt.getTask(sourceDeviceTaskId) : undefined
  const sourceDeviceData = sourceDeviceTask?._deviceData
  if (sourceDeviceData?.plans && Array.isArray(sourceDeviceData.plans)) {
    sourceDeviceData.plans = sourceDeviceData.plans.filter((item: any) => {
      const itemIdentity = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
      return itemIdentity !== planIdentity
    })
  }
  if (!Array.isArray(targetDeviceData.plans)) {
    targetDeviceData.plans = []
  }
  const targetExists = targetDeviceData.plans.some((item: any) => {
    const itemIdentity = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
    return itemIdentity === planIdentity
  })
  if (!targetExists) {
    targetDeviceData.plans.push(task._planData)
  }
  task._planData.deviceId = targetDeviceData.deviceId
  task._planData.feedingPipeline = targetDeviceData.deviceId
  task._deviceData = targetDeviceData
}

const normalizeDeviceChildren = (deviceTaskId: string | number, priorityTaskId?: string | number) => {
  try {
    gantt.getTask(deviceTaskId)
  } catch {
    return
  }
  const childTaskIds = gantt.getChildren(deviceTaskId)
  if (!Array.isArray(childTaskIds) || !childTaskIds.length) return
  const childTasks = childTaskIds
    .map((childId: string | number) => gantt.getTask(childId))
    .filter((item: any) => item?._planData)
    .sort((a: any, b: any) => {
      const startDiff = dayjs(a.start_date).valueOf() - dayjs(b.start_date).valueOf()
      if (startDiff !== 0) return startDiff
      if (priorityTaskId !== undefined && String(a.id) === String(priorityTaskId)) return -1
      if (priorityTaskId !== undefined && String(b.id) === String(priorityTaskId)) return 1
      return 0
    })
  childTasks.forEach((item: any) => {
    syncPlanTimeFromTask(item)
    gantt.updateTask(item.id)
  })
  const fakePlanTask = { _planData: true, parent: deviceTaskId }
  syncDeviceTaskRangeFromChildren(fakePlanTask)
}

const refreshPlanLinksByRowOrder = () => {
  try {
    const allLinks = gantt.getLinks()
    allLinks.forEach((item: any) => gantt.deleteLink(item.id))
    let linkIndex = 1
    const deviceTaskIds = gantt.getChildren(0)
    deviceTaskIds.forEach((deviceTaskId: string | number) => {
      try {
        const childTaskIds = gantt.getChildren(deviceTaskId).filter((childId: string | number) => {
          const task = gantt.getTask(childId)
          return !!task?._planData
        })
        let previousTaskId: string | number | null = null
        childTaskIds.forEach((taskId: string | number) => {
          if (previousTaskId !== null) {
            gantt.addLink({
              id: `link-${linkIndex++}`,
              source: previousTaskId,
              target: taskId,
              type: '0'
            })
          }
          previousTaskId = taskId
        })
      } catch {}
    })
  } catch {}
}

const getDeviceInsertIndex = (deviceTaskId: string | number, startDate: dayjs.Dayjs) => {
  const childTaskIds = gantt.getChildren(deviceTaskId)
  if (!Array.isArray(childTaskIds) || !childTaskIds.length) return 0
  const targetStart = startDate.startOf('day').valueOf()
  for (let index = 0; index < childTaskIds.length; index += 1) {
    const childTask = gantt.getTask(childTaskIds[index])
    if (!childTask?._planData) continue
    const childStart = dayjs(childTask.start_date).startOf('day').valueOf()
    if (childStart >= targetStart) return index
  }
  return childTaskIds.length
}

const addDeviceToGantt = (device: any, excludePlanIdentity?: string) => {
  const deviceId = `device-${device.deviceId}`
  try {
    gantt.getTask(deviceId)
    return
  } catch {}
  const plans = (device?.plans ?? []).map((plan: any) => ({
    ...plan,
    _start: dayjs(plan?.planStartTimeStr),
    _end: dayjs(plan?.planEndTimeStr)
  }))
  const validPlans = plans
    .filter((plan: any) => plan._start.isValid() && plan._end.isValid())
    .sort((a: any, b: any) => a._start.valueOf() - b._start.valueOf())
  const firstPlan = validPlans[0]
  const deviceRange = getDeviceTaskRangeByChildren(
    validPlans.map((item: any) => ({ start_date: item?._start, end_date: item?._end }))
  )
  gantt.addTask({
    id: deviceId,
    text: `${device.deviceName ?? '-'}`,
    start_date: formatGanttDate(deviceRange?.start_date ?? firstPlan?._start),
    end_date: formatGanttDate(deviceRange?.end_date ?? firstPlan?._end),
    duration: deviceRange?.duration ?? 1,
    parent: 0,
    progress: 0,
    open: true,
    readonly: true,
    deviceName: device.deviceName ?? '-',
    _deviceData: device
  })
  validPlans.forEach((plan: any, index: number) => {
    const planIdentity = `${plan?.taskId ?? ''}-${plan?.taskDetailId ?? ''}`
    if (excludePlanIdentity && planIdentity === excludePlanIdentity) return
    const startDate = formatGanttDate(plan.planStartTimeStr)
    const endDate = formatGanttDate(plan.planEndTimeStr)
    if (!startDate || !endDate) return
    const duration = Number(plan.scheduleDays) > 0 ? Number(plan.scheduleDays) : Math.max(plan._end.diff(plan._start, 'day') + 1, 1)
    const isHistory = String(plan.sourceType ?? '').toUpperCase() === 'HISTORY'
    const planTaskId = `plan-${device.deviceId}-${plan.taskDetailId ?? index}-${index}`
    gantt.addTask({
      id: planTaskId,
      text: `${plan.productCode ?? '-'} / ${plan.productName ?? '-'} / ${plan.taskCode ?? '-'}`,
      start_date: startDate,
      end_date: endDate,
      duration,
      parent: deviceId,
      progress: 0,
      readonly: isHistory,
      _planData: plan,
      _deviceData: device
    })
  })
}

const removeDeviceFromGanttIfNoCurrent = (deviceTaskId: string | number) => {
  try {
    const deviceTask = gantt.getTask(deviceTaskId)
    if (!deviceTask || deviceTask?._planData) return
    const childTaskIds = gantt.getChildren(deviceTaskId)
    const hasCurrent = (Array.isArray(childTaskIds) ? childTaskIds : []).some((childId: string | number) => {
      const childTask = gantt.getTask(childId)
      return childTask?._planData && String(childTask._planData.sourceType ?? '').toUpperCase() === 'CURRENT'
    })
    if (!hasCurrent) {
      gantt.deleteTask(String(deviceTaskId))
    }
  } catch {}
}

const applyTaskAdjust = (task: any, targetDeviceTaskId: string | number, startDate: string, endDate: string) => {
  const sourceDeviceTaskId = task.parent
  const nextStart = dayjs(startDate)
  const nextEnd = dayjs(endDate)
  const duration = Math.max(nextEnd.diff(nextStart, 'day') + 1, 1)
  const planIdentity = `${task?._planData?.taskId ?? ''}-${task?._planData?.taskDetailId ?? ''}`

  const targetDeviceData = previewScheduleList.value.find((d: any) => `device-${d.deviceId}` === targetDeviceTaskId)
  if (!targetDeviceData) return

  const sourceDeviceData = task._deviceData
  if (sourceDeviceData?.plans && Array.isArray(sourceDeviceData.plans)) {
    sourceDeviceData.plans = sourceDeviceData.plans.filter((item: any) => {
      const itemId = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
      return itemId !== planIdentity
    })
  }
  if (!Array.isArray(targetDeviceData.plans)) {
    targetDeviceData.plans = []
  }
  const targetExists = targetDeviceData.plans.some((item: any) => {
    const itemId = `${item?.taskId ?? ''}-${item?.taskDetailId ?? ''}`
    return itemId === planIdentity
  })
  if (!targetExists) {
    targetDeviceData.plans.push(task._planData)
  }
  task._planData.deviceId = targetDeviceData.deviceId
  task._planData.feedingPipeline = targetDeviceData.deviceId
  task._deviceData = targetDeviceData

  let targetDeviceInGantt = true
  try {
    gantt.getTask(targetDeviceTaskId)
  } catch {
    targetDeviceInGantt = false
  }

  if (!targetDeviceInGantt) {
    addDeviceToGantt(targetDeviceData, planIdentity)
  }

  task.parent = targetDeviceTaskId
  task.start_date = nextStart.toDate()
  task.end_date = nextEnd.toDate()
  task.duration = duration
  const targetIndex = getDeviceInsertIndex(targetDeviceTaskId, nextStart)
  gantt.moveTask(task.id, targetIndex, targetDeviceTaskId)
  gantt.updateTask(task.id)
  syncPlanTimeFromTask(task)
  normalizeDeviceChildren(targetDeviceTaskId, task.id)
  if (sourceDeviceTaskId && sourceDeviceTaskId !== targetDeviceTaskId) {
    removeDeviceFromGanttIfNoCurrent(sourceDeviceTaskId)
    normalizeDeviceChildren(sourceDeviceTaskId)
  }
  refreshPlanLinksByRowOrder()
  refreshTimelineRangeByTasks()
  gantt.render()
}

const openTaskAdjustDialog = (task: any) => {
  if (!task?._planData) return
  taskAdjustTaskId.value = task.id
  taskAdjustForm.deviceTaskId = String(task.parent ?? '')
  taskAdjustForm.startDate = dayjs(task.start_date).format('YYYY-MM-DD HH:mm:ss')
  taskAdjustForm.endDate = dayjs(task.end_date).format('YYYY-MM-DD HH:mm:ss')
  taskAdjustDialogVisible.value = true
}

const handleTaskAdjustSubmit = () => {
  if (!taskAdjustTaskId.value) return
  if (!taskAdjustForm.deviceTaskId || !taskAdjustForm.startDate || !taskAdjustForm.endDate) {
    message.warning('请完善设备、计划开始日期和计划结束日期')
    return
  }
  const task = gantt.getTask(taskAdjustTaskId.value)
  if (!task?._planData) return
  ganttSyncing.value = true
  try {
    applyTaskAdjust(task, taskAdjustForm.deviceTaskId, taskAdjustForm.startDate, taskAdjustForm.endDate)
  } finally {
    ganttSyncing.value = false
  }
  taskAdjustDialogVisible.value = false
}

const refreshTimelineRangeByTasks = () => {
  let minStart = Number.POSITIVE_INFINITY
  let maxEnd = Number.NEGATIVE_INFINITY
  gantt.eachTask((task: any) => {
    if (!task?._planData) return
    const start = dayjs(task.start_date).valueOf()
    const end = dayjs(task.end_date).valueOf()
    if (!Number.isFinite(start) || !Number.isFinite(end)) return
    minStart = Math.min(minStart, start)
    maxEnd = Math.max(maxEnd, end)
  })
  if (!Number.isFinite(minStart) || !Number.isFinite(maxEnd)) return

  const nextStartDate = dayjs(minStart).startOf('day').toDate()
  const nextEndDate = dayjs(maxEnd).endOf('day').toDate()
  const currentStart = dayjs(gantt.config.start_date).valueOf()
  const currentEnd = dayjs(gantt.config.end_date).valueOf()
  const nextStart = dayjs(nextStartDate).valueOf()
  const nextEnd = dayjs(nextEndDate).valueOf()
  if (currentStart === nextStart && currentEnd === nextEnd) return

  gantt.config.start_date = nextStartDate
  gantt.config.end_date = nextEndDate
  gantt.render()
}

const initGanttPreview = () => {
  if (!ganttContainerRef.value) return
  destroyGantt()
  if (!previewScheduleList.value.length) {
    activePreviewDevice.value = undefined
    activePreviewTask.value = undefined
    return
  }

  gantt.plugins({ tooltip: true, inline_edit: true,undo: true })
  gantt.config.readonly = false
  gantt.config.drag_move = true
  gantt.config.drag_links = false
  gantt.config.drag_progress = false
  gantt.config.drag_resize = true
  gantt.config.order_branch = true
  gantt.config.order_branch_free = true
  gantt.config.details_on_dblclick = false
  gantt.config.show_progress = false
  gantt.config.row_height = 40
  gantt.config.scale_height = 44
  gantt.config.xml_date = '%Y-%m-%d %H:%i'
  gantt.config.task_height = 24

  gantt.config.columns = [
    {
      name: 'text',
      label: '任务名称',
      tree: true,
      width: '*',
      min_width: 200
    },
    {
      name: 'start_date',
      label: '计划时间',
      align: 'center',
      width: 210,
      template: (task: any) =>
        task?._planData && String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
          ? `<span class="gantt-inline-editor-trigger" data-field="start_date" data-task-id="${task.id}">${formatGridDateText(task.start_date)}</span>`
          : formatGridDateText(task.start_date)
    }
  ]

  gantt.config.scales = [
    { unit: 'month', step: 1, format: (date) => dayjs(date).format('YYYY年M月') },
    { unit: 'day', step: 1, format: (date) => dayjs(date).format('MM-DD') }
  ]

  gantt.templates.tooltip_text = (start, end, task: any) => buildTaskTooltipHtml(task, start, end)
  gantt.templates.task_class = (_start, _end, task: any) => {
    if (!task?._planData) return ''
    const sourceType = String(task?._planData?.sourceType ?? '').toUpperCase()
    return sourceType === 'HISTORY' ? 'schedule-plan-task-history' : 'schedule-plan-task'
  }

  const ganttScheduleData = getGanttScheduleList()
  const globalRange = getGlobalDateRange(ganttScheduleData)
  gantt.config.start_date = dayjs(globalRange.start).startOf('day').toDate()
  gantt.config.end_date = dayjs(globalRange.end).endOf('day').toDate()

  gantt.init(ganttContainerRef.value)

  const ganttData = buildPreviewGanttData(ganttScheduleData)
  gantt.parse(ganttData)
  
  // 强制刷新所有任务的样式，确保颜色正确应用
  gantt.eachTask((task: any) => {
    gantt.refreshTask(task.id)
  })
  
  initTaskTooltips()

  if (ganttData.data.length) {
    activePreviewDevice.value = ganttData.data[0]._deviceData
    const today = dayjs().startOf('day').toDate()
    const pos = gantt.posFromDate(today)
    const scrollState = gantt.getScrollState()
    if (scrollState && pos >= 0) {
      const halfWidth = scrollState.inner_width / 3
      gantt.scrollTo(Math.max(0, pos - halfWidth), 0)
    } else {
      gantt.showDate(today)
    }
  }

  const clickEventId = gantt.attachEvent('onTaskClick', (id, event: MouseEvent) => {
    const task = gantt.getTask(id)
    if (task?._planData) {
      activePreviewTask.value = task._planData
      activePreviewDevice.value = task._deviceData
    } else {
      activePreviewTask.value = undefined
      activePreviewDevice.value = task?._deviceData
    }
    const target = event?.target as HTMLElement | null
    const editableNode = target?.closest('.gantt-inline-editor-trigger') as HTMLElement | null
    const field = editableNode?.dataset?.field
    if (!field || !task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return true
    if (field === 'start_date') {
      openTaskAdjustDialog(task)
      return false
    }
    const inlineEditors = (gantt.ext as any)?.inlineEditors
    if (inlineEditors?.startEdit) {
      inlineEditors.startEdit(id, field)
      return false
    }
    return true
  })
  ganttEventIds.value.push(clickEventId)

  if (ganttContainerRef.value) {
    const gridClickHandler = (e: MouseEvent) => {
      const target = e.target as HTMLElement
      const trigger = target.closest('.gantt-inline-editor-trigger[data-field="start_date"]') as HTMLElement | null
      if (!trigger) return
      const taskId = trigger.getAttribute('data-task-id')
      if (!taskId) return
      try {
        const task = gantt.getTask(taskId)
        if (!task?._planData || String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return
        e.stopPropagation()
        openTaskAdjustDialog(task)
      } catch {}
    }
    ganttContainerRef.value.addEventListener('click', gridClickHandler, true)
    tooltipCleanupFns.value.push(() => {
      ganttContainerRef.value?.removeEventListener('click', gridClickHandler, true)
    })
  }

  const beforeDragEventId = gantt.attachEvent('onBeforeTaskDrag', (id) => {
    const task = gantt.getTask(id)
    if (!task?._planData) return false
    taskDragDurationMap.value[String(id)] = Math.max(Number(task.duration) || 1, 1)
    return String(task?._planData?.sourceType ?? '').toUpperCase() === 'CURRENT'
  })

  const beforeMoveEventId = gantt.attachEvent('onBeforeTaskMove', (id, parent) => {
    const task = gantt.getTask(id)
    if (!task?._planData) return false
    if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return false
    const targetTask = gantt.getTask(parent)
    if (!targetTask || targetTask?._planData) return false
    taskMoveFromParentMap.value[String(id)] = task.parent
    return true
  })

  const afterMoveEventId = gantt.attachEvent('onAfterTaskMove', (id, parent) => {
    const task = gantt.getTask(id)
    if (!task?._planData) return
    const sourceParent = taskMoveFromParentMap.value[String(id)]
    delete taskMoveFromParentMap.value[String(id)]
    if (!sourceParent && sourceParent !== 0) return
    if (sourceParent === parent) {
      refreshPlanLinksByRowOrder()
      return
    }
    if (ganttSyncing.value) return
    ganttSyncing.value = true
    try {
      movePlanDataToDevice(task, parent, sourceParent)
      syncPlanTimeFromTask(task)
      normalizeDeviceChildren(parent)
      normalizeDeviceChildren(sourceParent)
      removeDeviceFromGanttIfNoCurrent(sourceParent)
      refreshPlanLinksByRowOrder()
      refreshTimelineRangeByTasks()
    } finally {
      ganttSyncing.value = false
    }
  })

  const contextMenuEventId = gantt.attachEvent('onContextMenu', (taskId, _linkId, event: MouseEvent) => {
    const task = gantt.getTask(taskId)
    if (!task?._planData) return true
    if (String(task?._planData?.sourceType ?? '').toUpperCase() !== 'CURRENT') return false
    event.preventDefault()
    openTaskAdjustDialog(task)
    return false
  })

  const dragEventId = gantt.attachEvent('onAfterTaskDrag', (id, mode) => {
    if (ganttSyncing.value) return
    const task = gantt.getTask(id)
    const taskId = String(id)
    const originalDuration = taskDragDurationMap.value[taskId]
    delete taskDragDurationMap.value[taskId]
    ganttSyncing.value = true
    try {
      if (mode === gantt.config.drag_mode.move && Number(originalDuration) > 0) {
        task.duration = originalDuration
      }
      syncPlanTimeFromTask(task)
      normalizeDeviceChildren(task.parent)
      refreshPlanLinksByRowOrder()
      refreshTimelineRangeByTasks()
    } finally {
      ganttSyncing.value = false
    }
  })

  const updateEventId = gantt.attachEvent('onAfterTaskUpdate', (id) => {
    if (ganttSyncing.value) return
    const task = gantt.getTask(id)
    ganttSyncing.value = true
    try {
      syncPlanTimeFromTask(task)
      normalizeDeviceChildren(task.parent)
      refreshPlanLinksByRowOrder()
      refreshTimelineRangeByTasks()
      gantt.refreshTask(id)
    } finally {
      ganttSyncing.value = false
    }
  })

  ganttEventIds.value.push(beforeDragEventId, beforeMoveEventId, afterMoveEventId, contextMenuEventId, dragEventId, updateEventId)
  refreshPlanLinksByRowOrder()
}

onMounted(async () => {
  await nextTick()
  initGanttPreview()
})

watch(
  () => props.scheduleList,
  async () => {
    if (ganttSyncing.value) return
    await nextTick()
    if (ganttSyncing.value) return
    initGanttPreview()
  },
  { deep: true }
)

onBeforeUnmount(() => {
  destroyGantt()
})
</script>

<style scoped>
.schedule-preview-wrap {
  display: flex;
  width: 100%;
  gap: 12px;
}

.schedule-gantt-container {
  flex: 1;
  min-width: 0;
  border: 1px solid var(--el-border-color);
  border-radius: 4px;
  overflow: hidden;
}

.schedule-detail-panel {
  width: 280px;
  flex: 0 0 280px;
}

.schedule-detail-title {
  margin-bottom: 10px;
  font-weight: 600;
}

.schedule-plan-list-title {
  margin-top: 14px;
  margin-bottom: 10px;
  font-weight: 600;
}

.schedule-plan-list {
  overflow: auto;
  max-height: 55vh;
  border: 1px solid var(--el-border-color);
  border-radius: 4px;
  padding: 8px;
}

.schedule-plan-item {
  padding: 8px;
  border-radius: 4px;
  background: var(--el-fill-color-light);
}
.schedule-plan-item-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}
.schedule-plan-item-title {
  font-weight: 600;
  color: var(--el-text-color-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.schedule-plan-item-active {
  border: 1px solid var(--el-color-success-light-5);
  background: var(--el-color-success-light-9);
}

.schedule-plan-item + .schedule-plan-item {
  margin-top: 8px;
}

.schedule-gantt-container :deep(.gantt_grid_data .gantt_row) {
  font-weight: 600;
}

.schedule-gantt-container :deep(.gantt_grid_data .gantt_cell) {
  text-overflow: clip;
}

.schedule-gantt-container :deep(.gantt_task_bg .gantt_task_cell) {
  border-right-width: 2px;
}

.schedule-gantt-container :deep(.gantt-inline-editor-trigger) {
  display: inline-block;
  width: 100%;
  overflow: visible;
  cursor: pointer;
}

.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task) {
  background: #67c23a;
  border-color: #67c23a;
}

.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task .gantt_task_content) {
  color: #ffffff;
}

.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-history) {
  background: #909399;
  border-color: #909399;
}

.schedule-gantt-container :deep(.gantt_task_line.schedule-plan-task-history .gantt_task_content) {
  color: #ffffff;
}

:deep(.gantt_tooltip) {
  z-index: 5000 !important;
}
</style>
