<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed, watch, useSlots, inject, h } from 'vue'
import type { StyleValue, Slots } from 'vue'
import { useI18n } from '../composables/useI18n'
import { formatPredecessorDisplay } from '../utils/predecessorUtils'
import type { Task } from '../models/classes/Task'
import type { TaskListColumnConfig } from '../models/configs/TaskListConfig'
import type { DeclarativeColumnConfig } from '../composables/useTaskListColumns'
import TaskContextMenu from './TaskContextMenu.vue'

interface TaskRowSlotProps {
  isRowContent: boolean
  task: Task
  level: number
  indent: string
  isHovered: boolean
  hoveredTaskId: number | null
  isParent: boolean
  hasChildren: boolean
  collapsed: boolean
  formattedTimer: string
  timerRunning: boolean
  timerElapsed: number
  isOvertime: number | boolean | undefined
  overdueDays: number
  overtimeText: string
  overdueText: string
  daysText: string
  progressClass: string
  type?: string
}

interface Props {
  task: Task
  level: number
  rowIndex?: number
  isHovered?: boolean
  hoveredTaskId?: number | null
  onHover?: (taskId: number | null) => void
  columns: TaskListColumnConfig[]
  declarativeColumns?: DeclarativeColumnConfig[]
  renderMode?: 'default' | 'declarative'
  getColumnWidthStyle?: (column: { width?: number | string }) => StyleValue
  disableChildrenRender?: boolean
  showTaskIcon?: boolean // 是否显示任务图标，默认true
  enableDrag?: boolean
  dragStart?: (task: Task, element: HTMLElement, event: MouseEvent) => void
  dragOver?: (task: Task, element: HTMLElement, event: MouseEvent) => void
  taskListRowClassName?: string | ((row: Task, rowIndex: number) => string)
  taskListRowStyle?: StyleValue | ((row: Task, rowIndex: number) => StyleValue)
}
const props = withDefaults(defineProps<Props>(), {
  renderMode: 'default',
})
const emit = defineEmits([
  'toggle',
  'dblclick',
  'contextmenu',
  'start-timer',
  'stop-timer',
  'add-predecessor',
  'add-successor',
  'delete',
])

defineSlots<{
  'custom-task-content'(props: TaskRowSlotProps): unknown
}>()
const { t } = useI18n()
const overtimeText = computed(() => t.value?.overtime ?? '')
const overdueText = computed(() => t.value?.overdue ?? '')
const daysText = computed(() => t.value?.days ?? '')

const slots = useSlots()
const hasContentSlot = computed(() => Boolean(slots['custom-task-content']))

// 从 GanttChart 注入列级 slots
const columnSlots = inject<Slots>('gantt-column-slots', {})

// 检查指定列是否有自定义 slot（从注入的 slots 中查找）
const hasColumnSlot = (columnKey: string) => {
  const slotName = `column-${columnKey}`
  return Boolean(columnSlots[slotName])
}

// 渲染列级 slot
const renderColumnSlot = (columnKey: string, slotProps: any) => {
  const slotName = `column-${columnKey}`
  const slotFn = columnSlots[slotName]
  if (slotFn) {
    return slotFn(slotProps)
  }
  return null
}

// 计算实际要渲染的列
const columnsToRender = computed(() => {
  if (props.renderMode === 'declarative' && props.declarativeColumns) {
    return props.declarativeColumns
  }
  return props.columns
})

// 判断是否是第一列
const isFirstColumn = (index: number) => index === 0

// 获取声明式列的对齐样式
const getDeclarativeColumnAlign = (column: DeclarativeColumnConfig) => {
  // if (!column.align || column.align === 'left') return {}
  return {
    justifyContent: column.align === 'center' ? 'center' : column.align === 'right' ? 'flex-end' : 'flex-start',
    textAlign: column.align || 'left',
  }
}

// 渲染声明式列的内容
const renderDeclarativeColumn = (
  column: DeclarativeColumnConfig,
  index: number,
): any => {
  // 第一列特殊处理：需要显示折叠按钮、图标等
  if (isFirstColumn(index)) {
    // 获取列对应的值（使用 prop 或默认为 name）
    const columnValue = column.prop ? (props.task as any)[column.prop] : props.task.name

    // 如果有自定义 slot
    if (column.defaultSlot) {
      return h('div', { class: 'task-name-content' }, [
        // 自定义内容
        h(
          'span',
          {
            class: ['task-name-text', { 'parent-task': isParentTask.value }],
            title: columnValue,
          },
          column.defaultSlot({
            row: props.task,
            $index: index,
          }),
        ),
      ])
    }

    // 默认显示列的值（使用 prop）
    return h('div', { class: 'task-name-content' }, [
      // 任务图标
      props.showTaskIcon !== false
        ? h(
            'span',
            { class: 'task-icon' },
            isMilestoneGroup.value
              ? h(
                  'svg',
                  {
                    width: 16,
                    height: 16,
                    viewBox: '0 0 24 24',
                    fill: 'none',
                    stroke: 'currentColor',
                    'stroke-width': 2,
                    class: 'milestone-group-icon',
                  },
                  h('polygon', { points: '12,2 22,12 12,22 2,12' }),
                )
              : isStoryTask.value || hasChildren.value
                ? h(
                    'svg',
                    {
                      width: 16,
                      height: 16,
                      viewBox: '0 0 24 24',
                      fill: 'none',
                      stroke: 'currentColor',
                      'stroke-width': 2,
                    },
                    h('path', {
                      d: 'M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z',
                    }),
                  )
                : h(
                    'svg',
                    {
                      width: 16,
                      height: 16,
                      viewBox: '0 0 24 24',
                      fill: 'none',
                      stroke: 'currentColor',
                      'stroke-width': 2,
                    },
                    [
                      h('path', {
                        d: 'M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z',
                      }),
                      h('polyline', { points: '14,2 14,8 20,8' }),
                      h('line', { x1: 16, y1: 13, x2: 8, y2: 13 }),
                      h('line', { x1: 16, y1: 17, x2: 8, y2: 17 }),
                      h('polyline', { points: '10,9 9,9 8,9' }),
                    ],
                  ),
          )
        : null,
      // 列的值（使用 prop 或默认 name）
      h(
        'span',
        {
          class: ['task-name-text', { 'parent-task': isParentTask.value }],
          title: columnValue,
        },
        columnValue || '-',
      ),
    ])
  }

  // 其他列：正常处理
  // 如果有自定义 slot，使用 slot
  if (column.defaultSlot) {
    return column.defaultSlot({
      row: props.task,
      $index: index,
    })
  }

  // 否则使用 prop 访问任务数据
  if (column.prop) {
    const value = (props.task as any)[column.prop]
    return value !== undefined && value !== null ? value : '-'
  }

  return '-'
}

const baseIndent = 10
const indent = computed(() => `${baseIndent + props.level * 20}px`)
const hasChildren = computed(() => !!(props.task.children && props.task.children.length > 0))
const isStoryTask = computed(() => props.task.type === 'story')
const isMilestoneGroup = computed(() => props.task.type === 'milestone-group')
const isMilestoneTask = computed(() => props.task.type === 'milestone')
const isParentTask = computed(
  () => isStoryTask.value || hasChildren.value || isMilestoneGroup.value,
)

// 计算自定义行类名
const customRowClass = computed(() => {
  if (!props.taskListRowClassName) return ''

  if (typeof props.taskListRowClassName === 'function') {
    return props.taskListRowClassName(props.task, props.rowIndex ?? 0)
  }

  return props.taskListRowClassName
})

// 计算自定义行样式（优先级高于类名）
const customRowStyle = computed(() => {
  if (!props.taskListRowStyle) return {}

  if (typeof props.taskListRowStyle === 'function') {
    return props.taskListRowStyle(props.task, props.rowIndex ?? 0)
  }

  return props.taskListRowStyle
})

function handleToggle() {
  emit('toggle', props.task)
}

function handleRowClick() {
  // 如果是普通父级任务（story类型或有子任务的任务，非里程碑分组），点击行也可以展开/收起
  if ((isStoryTask.value || hasChildren.value) && !isMilestoneGroup.value) {
    emit('toggle', props.task)
  }
}

// 处理TaskRow双击事件 (与TaskBar逻辑保持一致)
const handleTaskRowDoubleClick = (e: MouseEvent) => {
  // 阻止事件冒泡
  e.stopPropagation()

  // 发出双击事件给父组件
  emit('dblclick', props.task)
}

// 处理悬停事件
const handleMouseEnter = () => {
  // 如果正在拖拽Splitter，忽略悬停事件
  if (isSplitterDragging.value) return

  if (props.onHover) {
    props.onHover(props.task.id)
  }
}

const handleMouseLeave = () => {
  // 如果正在拖拽Splitter，忽略悬停事件
  if (isSplitterDragging.value) return

  if (props.onHover) {
    props.onHover(null)
  }
}

// 获取进度值的样式类
function getProgressClass() {
  const progress = props.task.progress || 0
  const today = new Date()
  const endDate = props.task.endDate ? new Date(props.task.endDate) : null

  // 超期且未完成
  if (endDate && today > endDate && progress < 100) {
    return 'progress-danger'
  }

  // 已完成
  if (progress >= 100) {
    return 'progress-success'
  }

  // 进行中
  if (progress > 0) {
    return 'progress-warning'
  }

  return ''
}

// 检查是否超时
function isOvertime() {
  return (
    props.task.actualHours &&
    props.task.estimatedHours &&
    props.task.actualHours > props.task.estimatedHours
  )
}

// 检查是否逾期，返回天数
function overdueDays() {
  const today = new Date()
  const endDate = props.task.endDate ? new Date(props.task.endDate) : null
  const progress = props.task.progress || 0
  if (endDate && today > endDate && progress < 100) {
    // 只计算日期部分
    const t = new Date(today.getFullYear(), today.getMonth(), today.getDate())
    const e = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate())
    const diff = Math.floor((t.getTime() - e.getTime()) / (1000 * 60 * 60 * 24))
    return diff
  }
  return 0
}

// 拖拽状态管理
const isSplitterDragging = ref(false)

// 处理拖拽开始事件
const handleSplitterDragStart = () => {
  isSplitterDragging.value = true
}

// 处理拖拽结束事件
const handleSplitterDragEnd = () => {
  isSplitterDragging.value = false
}

// 任务计时器状态
const timerElapsed = ref(0)
const timerInterval = ref<number | null>(null)

// 格式化计时器显示：转换为 HH:MM:SS 格式
const formattedTimer = computed(() => {
  const totalSeconds = Math.floor(timerElapsed.value / 1000)
  const hours = Math.floor(totalSeconds / 3600)
  const minutes = Math.floor((totalSeconds % 3600) / 60)
  const seconds = totalSeconds % 60
  return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
})

// 更新计时器显示
const updateTimer = () => {
  if (props.task.isTimerRunning && props.task.timerStartTime) {
    // 计算已经运行的时间 = 当前时间 - 开始时间 + 之前累积的时间
    const previousElapsed = props.task.timerElapsedTime || 0
    timerElapsed.value = Date.now() - props.task.timerStartTime + previousElapsed
  } else if (props.task.timerElapsedTime) {
    // 如果任务不在运行中，但有累计时间，显示累计时间
    timerElapsed.value = props.task.timerElapsedTime
  } else {
    // 默认情况下，计时器为0
    timerElapsed.value = 0
  }
}

// 监听任务的计时状态变化
watch(
  () => [props.task.isTimerRunning, props.task.timerStartTime, props.task.timerElapsedTime],
  () => {
    // 清除之前的计时器
    if (timerInterval.value) {
      clearInterval(timerInterval.value)
      timerInterval.value = null
    }

    // 如果任务正在计时，开始计时器
    if (props.task.isTimerRunning) {
      updateTimer()
      timerInterval.value = window.setInterval(updateTimer, 1000)
    } else {
      // 更新一次最终值
      updateTimer()
    }
  },
  { immediate: true },
)

// 右键菜单相关状态
const contextMenuVisible = ref(false)
const contextMenuPosition = ref({ x: 0, y: 0 })
const contextMenuTask = computed(() => props.task)

// 处理右键菜单显示
function handleContextMenu(event: MouseEvent) {
  // 先广播关闭所有TaskRow菜单
  window.dispatchEvent(new CustomEvent('close-all-taskbar-menus'))
  if (props.task.type !== 'task' && props.task.type !== 'story') {
    // 为了排除里程碑类型
    event.preventDefault()
    contextMenuVisible.value = false
    return
  }
  event.preventDefault()
  contextMenuVisible.value = true
  contextMenuPosition.value = { x: event.clientX, y: event.clientY }
}

// 关闭右键菜单
function closeContextMenu() {
  contextMenuVisible.value = false
}

const handleTaskDelete = (task: Task, deleteChildren?: boolean) => {
  // 触发删除事件
  emit('delete', task, deleteChildren)
  closeContextMenu()
}

const progressClass = computed(() => getProgressClass())

const slotPayload = computed(() => ({
  isRowContent: true,
  task: props.task,
  level: props.level,
  indent: indent.value,
  isHovered: props.isHovered ?? false,
  hoveredTaskId: props.hoveredTaskId ?? null,
  isParent: isParentTask.value,
  hasChildren: hasChildren.value,
  collapsed: !!props.task.collapsed,
  formattedTimer: formattedTimer.value,
  timerRunning: !!props.task.isTimerRunning,
  timerElapsed: timerElapsed.value,
  isOvertime: isOvertime(),
  overdueDays: overdueDays(),
  overtimeText: overtimeText.value,
  overdueText: overdueText.value,
  daysText: daysText.value,
  progressClass: progressClass.value,
}))

// TaskRow拖拽功能
const taskRowRef = ref<HTMLElement | null>(null)

const handleMouseDown = (event: MouseEvent) => {
  // 如果未启用拖拽，直接返回
  if (!props.enableDrag) return

  // 如果点击的是按钮、输入框等交互元素，不触发拖拽
  const target = event.target as HTMLElement
  if (
    target.tagName === 'BUTTON' ||
    target.tagName === 'INPUT' ||
    target.tagName === 'SELECT' ||
    target.tagName === 'TEXTAREA' ||
    target.closest('.collapse-btn') ||
    target.closest('.task-context-menu')
  ) {
    return
  }

  if (taskRowRef.value && props.dragStart) {
    props.dragStart(props.task, taskRowRef.value, event)
  }
}

// 处理全局拖拽悬停事件
const handleTaskRowDragOver = (event: CustomEvent) => {
  if (!props.enableDrag || !taskRowRef.value || !props.dragOver) return

  const { taskId, event: mouseEvent } = event.detail
  if (taskId === props.task.id) {
    props.dragOver(props.task, taskRowRef.value, mouseEvent)
  }
}

// 生命周期钩子 - 注册事件监听器
onMounted(() => {
  window.addEventListener('splitter-drag-start', handleSplitterDragStart)
  window.addEventListener('splitter-drag-end', handleSplitterDragEnd)
  window.addEventListener('close-all-taskbar-menus', closeContextMenu)
  window.addEventListener('task-row-drag-over', handleTaskRowDragOver as EventListener)
})

onUnmounted(() => {
  window.removeEventListener('splitter-drag-start', handleSplitterDragStart)
  window.removeEventListener('splitter-drag-end', handleSplitterDragEnd)
  window.removeEventListener('close-all-taskbar-menus', closeContextMenu)
  window.removeEventListener('task-row-drag-over', handleTaskRowDragOver as EventListener)
  if (timerInterval.value) {
    clearInterval(timerInterval.value)
  }
})
</script>

<template>
  <div>
    <div
      ref="taskRowRef"
      class="task-row"
      :data-task-id="props.task.id"
      :class="{
        'task-row-hovered': isHovered,
        'parent-task': isParentTask,
        'milestone-group-row': isMilestoneGroup,
        'task-type-story': isStoryTask,
        'task-type-task': props.task.type === 'task',
        'task-type-milestone': isMilestoneTask,
        [customRowClass]: customRowClass,
      }"
      :style="customRowStyle"
      @click="handleRowClick"
      @dblclick="handleTaskRowDoubleClick"
      @mousedown="handleMouseDown"
      @mouseenter="handleMouseEnter"
      @mouseleave="handleMouseLeave"
      @contextmenu="handleContextMenu"
    >
      <!-- 声明式渲染模式 -->
      <template v-if="renderMode === 'declarative' && declarativeColumns">
        <div
          v-for="(column, index) in columnsToRender"
          :key="index"
          class="col"
          :class="[column.cssClass, { 'col-name': isFirstColumn(index) }]"
          :style="{
            ...(getColumnWidthStyle ? getColumnWidthStyle(column) : {}),
            ...getDeclarativeColumnAlign(column),
          }"
        >
          <!-- 第一列：保留 collapse-btn, milestone-spacer, leaf-spacer -->
          <div v-if="isFirstColumn(index)"
              :style="{ paddingLeft: indent }"
              class="first-col-wrapper">
            <span
              v-if="(isStoryTask || hasChildren) && !isMilestoneGroup"
              class="collapse-btn"
              @click.stop="handleToggle"
            >
              <svg
                width="12"
                height="12"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
              >
                <polyline v-if="props.task.collapsed" points="9,18 15,12 9,6" />
                <polyline v-else points="18,15 12,9 6,15" />
              </svg>
            </span>

            <span v-if="isMilestoneGroup" class="milestone-spacer"></span>

            <span
              v-if="!isParentTask && !isMilestoneGroup && showTaskIcon === false"
              class="leaf-spacer"
            ></span>

            <!-- 渲染列内容 -->
            <component :is="() => renderDeclarativeColumn(column, index)" />
          </div>

          <!-- 其他列：直接渲染内容 -->
          <component v-else :is="() => renderDeclarativeColumn(column, index)" />
        </div>
      </template>

      <!-- 默认渲染模式 -->
      <template v-else>
        <div class="col col-name" :style="{ paddingLeft: indent }">
          <span
            v-if="(isStoryTask || hasChildren) && !isMilestoneGroup"
            class="collapse-btn"
            @click.stop="handleToggle"
          >
            <svg
              width="12"
              height="12"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <polyline v-if="props.task.collapsed" points="9,18 15,12 9,6" />
              <polyline v-else points="18,15 12,9 6,15" />
            </svg>
          </span>

          <!-- 里程碑分组的占位空间，用于与有折叠按钮的任务对齐 -->
          <span v-if="isMilestoneGroup" class="milestone-spacer"></span>

          <!-- 叶子节点的占位空间（无折叠按钮且无图标显示时） -->
          <span
            v-if="!isParentTask && !isMilestoneGroup && showTaskIcon === false"
            class="leaf-spacer"
          ></span>

        <!-- 优先级1: 列级自定义 Slot (#column-name) - 覆盖整列内容（图标+文本+徽章） -->
        <component
          :is="() => renderColumnSlot('name', {
            task: props.task,
            column: { key: 'name' },
            value: props.task.name,
            isParentTask,
            hasChildren,
            isStoryTask: isStoryTask.value,
            isMilestoneGroup: isMilestoneGroup.value,
            isMilestoneTask: isMilestoneTask.value,
            showTaskIcon: props.showTaskIcon,
            formattedTimer: formattedTimer.value,
            isOvertime: isOvertime(),
            overdueDays: overdueDays(),
            overtimeText: overtimeText.value,
            overdueText: overdueText.value,
            daysText: daysText.value,
          })"
          v-if="hasColumnSlot('name')"
        />

        <!-- 优先级2: custom-task-content Slot (向后兼容) - 仅替换文本部分 -->
        <template v-else-if="hasContentSlot">
          <!-- 任务图标 -->
          <span v-if="showTaskIcon !== false" class="task-icon">
            <!-- 里程碑分组图标 - 使用菱形图标 -->
            <svg
              v-if="isMilestoneGroup"
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              class="milestone-group-icon"
            >
              <polygon points="12,2 22,12 12,22 2,12" />
            </svg>
            <!-- 父级任务图标 -->
            <svg
              v-else-if="isStoryTask || hasChildren"
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z" />
            </svg>
            <!-- 普通任务图标 -->
            <svg
              v-else
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
              <polyline points="14,2 14,8 20,8" />
              <line x1="16" y1="13" x2="8" y2="13" />
              <line x1="16" y1="17" x2="8" y2="17" />
              <polyline points="10,9 9,9 8,9" />
            </svg>
          </span>

          <span
            class="task-name-text"
            :class="{ 'parent-task': isParentTask }"
            :title="props.task.name"
          >
            <slot
              name="custom-task-content"
              v-bind="slotPayload"
              type="task-row"
            />
            <!-- 计时器显示 -->
            <span
              v-if="props.task.isTimerRunning || props.task.timerElapsedTime"
              class="timer-badge"
              :class="{ 'timer-active': props.task.isTimerRunning }"
            >
              <span v-if="props.task.isTimerRunning" class="timer-dot"></span>
              {{ formattedTimer }}
            </span>
            <span v-if="isOvertime()" class="status-badge overtime">{{ overtimeText }}</span>
            <span v-if="overdueDays() > 0" class="status-badge overdue">
              {{ overdueText }}{{ overdueDays() > 0 ? overdueDays() + daysText : '' }}
            </span>
          </span>
        </template>

        <!-- 优先级3: 默认渲染 -->
        <template v-else>
          <!-- 任务图标 -->
          <span v-if="showTaskIcon !== false" class="task-icon">
            <!-- 里程碑分组图标 - 使用菱形图标 -->
            <svg
              v-if="isMilestoneGroup"
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              class="milestone-group-icon"
            >
              <polygon points="12,2 22,12 12,22 2,12" />
            </svg>
            <!-- 父级任务图标 -->
            <svg
              v-else-if="isStoryTask || hasChildren"
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <path d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z" />
            </svg>
            <!-- 普通任务图标 -->
            <svg
              v-else
              width="16"
              height="16"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" />
              <polyline points="14,2 14,8 20,8" />
              <line x1="16" y1="13" x2="8" y2="13" />
              <line x1="16" y1="17" x2="8" y2="17" />
              <polyline points="10,9 9,9 8,9" />
            </svg>
          </span>

          <span
            class="task-name-text"
            :class="{ 'parent-task': isParentTask }"
            :title="props.task.name"
          >
            {{ props.task.name }}
            <!-- 计时器显示 -->
            <span
              v-if="props.task.isTimerRunning || props.task.timerElapsedTime"
              class="timer-badge"
              :class="{ 'timer-active': props.task.isTimerRunning }"
            >
              <span v-if="props.task.isTimerRunning" class="timer-dot"></span>
              {{ formattedTimer }}
            </span>
            <span v-if="isOvertime()" class="status-badge overtime">{{ overtimeText }}</span>
            <span v-if="overdueDays() > 0" class="status-badge overdue">
              {{ overdueText }}{{ overdueDays() > 0 ? overdueDays() + daysText : '' }}
            </span>
          </span>
        </template>
      </div>

      <!-- 动态渲染列 -->
      <div
        v-for="column in columns"
        :key="column.key"
        class="col"
        :class="column.cssClass || `col-${column.key}`"
        :style="getColumnWidthStyle ? getColumnWidthStyle(column) : undefined"
      >
        <!-- 里程碑分组显示空列 -->
        <template v-if="isMilestoneGroup">
          <div class="milestone-empty-col"></div>
        </template>
        <!-- 普通任务显示具体内容 -->
        <template v-else>
          <!-- 优先级1: 列级自定义 Slot (约定: #column-{key})，通过 renderColumnSlot 动态渲染 -->
          <component
            :is="() =>
            renderColumnSlot(column.key,
                            { task: props.task, column, value: (props.task as any)[column.key] })"
            v-if="hasColumnSlot(column.key)"
          />

          <!-- 优先级2: Formatter 函数 -->
          <template v-else-if="column.formatter">
            {{ column.formatter(props.task, column) }}
          </template>

          <!-- 优先级3: 内置列类型渲染 -->
          <!-- 前置任务列 -->
          <template v-else-if="column.key === 'predecessor'">
            {{ formatPredecessorDisplay(props.task.predecessor) }}
          </template>

          <!-- 负责人列 -->
          <template v-else-if="column.key === 'assignee'">
            <div class="assignee-info">
              <div class="avatar">
                {{ props.task.assignee ? props.task.assignee.charAt(0) : '-' }}
              </div>
              <span class="assignee-name">{{ props.task.assignee || '-' }}</span>
            </div>
          </template>

          <!-- 开始日期列 -->
          <template v-else-if="column.key === 'startDate'">
            {{ props.task.startDate || '-' }}
          </template>

          <!-- 结束日期列 -->
          <template v-else-if="column.key === 'endDate'">
            {{ props.task.endDate || '-' }}
          </template>

          <!-- 预估工时列 -->
          <template v-else-if="column.key === 'estimatedHours'">
            {{ props.task.estimatedHours || '-' }}
          </template>

          <!-- 实际工时列 -->
          <template v-else-if="column.key === 'actualHours'">
            {{ props.task.actualHours || '-' }}
          </template>

          <!-- 进度列 -->
          <template v-else-if="column.key === 'progress'">
            <span class="progress-value" :class="progressClass">
              {{ props.task.progress != null ? props.task.progress + '%' : '-' }}
            </span>
          </template>

          <!-- 优先级4: 默认渲染 - 自定义列通过task对象的key动态获取值 -->
          <template v-else>
            {{ (props.task as any)[column.key] || '-' }}
          </template>
        </template>
      </div>
      </template>
      <!-- 结束默认渲染模式 -->
    </div>
    <template
      v-if="
        !props.disableChildrenRender && hasChildren && !props.task.collapsed && !isMilestoneGroup
      "
    >
      <TaskRow
        v-for="child in props.task.children"
        :key="child.id"
        :task="child"
        :level="props.level + 1"
        :is-hovered="props.hoveredTaskId === child.id"
        :hovered-task-id="props.hoveredTaskId"
        :on-hover="props.onHover"
        :columns="props.columns"
        :declarative-columns="props.declarativeColumns"
        :render-mode="props.renderMode"
        :get-column-width-style="props.getColumnWidthStyle"
        :disable-children-render="props.disableChildrenRender"
        :show-task-icon="props.showTaskIcon"
        :enable-drag="props.enableDrag"
        :drag-start="props.dragStart"
        :drag-over="props.dragOver"
        @toggle="emit('toggle', $event)"
        @dblclick="emit('dblclick', $event)"
        @start-timer="emit('start-timer', $event)"
        @stop-timer="emit('stop-timer', $event)"
        @add-predecessor="emit('add-predecessor', $event)"
        @add-successor="emit('add-successor', $event)"
        @delete="handleTaskDelete"
      >
        <!-- 传递 custom-task-content slot -->
        <template v-if="hasContentSlot" #custom-task-content="slotProps">
          <slot name="custom-task-content" v-bind="slotProps as TaskRowSlotProps" />
        </template>
      </TaskRow>
    </template>

    <TaskContextMenu
      :visible="contextMenuVisible"
      :task="contextMenuTask"
      :position="contextMenuPosition"
      @close="closeContextMenu"
      @start-timer="$emit('start-timer', props.task)"
      @stop-timer="$emit('stop-timer', props.task)"
      @add-predecessor="$emit('add-predecessor', props.task)"
      @add-successor="$emit('add-successor', props.task)"
      @delete="handleTaskDelete"
    />
  </div>
</template>

<style scoped>
@import '../styles/theme-variables.css';
@import '../styles/list.css';

.task-row {
  display: flex;
  border-bottom: 1px solid var(--gantt-border-light);
  height: 51px; /* 修改为51px，与Timeline中的task-row高度保持一致，包含border-bottom 1px */
  box-sizing: border-box; /* 确保border包含在高度计算中 */
  background: var(--gantt-bg-primary);
  align-items: center;
  color: var(--gantt-text-secondary);
  cursor: pointer;
  transition: all 0.3s ease;
  transform: scale(1);
  transform-origin: 5px center; /* 从左侧偏右5px的位置作为放大中心 */
  z-index: 1;
  position: relative;
}

.task-row:hover {
  background-color: var(--gantt-bg-hover);
  transform: scale(1.02);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 10;
}

.task-row-hovered {
  background-color: var(--gantt-bg-hover) !important;
  transform: scale(1.02) !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
  z-index: 10 !important;
}

.task-row.parent-task {
  background: var(--gantt-bg-tertiary);
  font-weight: 600;
}

.task-row.parent-task:hover {
  background: var(--gantt-bg-hover-parent, var(--gantt-bg-hover));
  transform: scale(1.02);
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
  z-index: 10;
}

.task-row.parent-task.task-row-hovered {
  background: var(--gantt-bg-hover-parent, var(--gantt-bg-hover)) !important;
  transform: scale(1.02) !important;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2) !important;
  z-index: 10 !important;
}

/* 里程碑分组行特殊样式 - 使用红色边框 */
.milestone-group-row {
  border-left: 3px solid var(--gantt-danger, #f56c6c);
  background: linear-gradient(90deg, var(--gantt-bg-tertiary) 0%, var(--gantt-bg-primary) 100%);
}

.milestone-group-row:hover {
  background: linear-gradient(90deg, var(--gantt-bg-hover-parent) 0%, var(--gantt-bg-hover) 100%);
  transform: scale(1.02);
  box-shadow:
    0 6px 16px rgba(245, 108, 108, 0.3),
    0 2px 8px rgba(0, 0, 0, 0.1);
  z-index: 10;
  border-left-color: var(--gantt-danger, #f56c6c);
  border-left-width: 4px; /* 悬停时边框稍微加粗 */
}

/* 任务类型左边框颜色 */
.task-type-story {
  border-left: 3px solid var(--gantt-primary, #409eff);
}

.task-type-task {
  border-left: 3px solid var(--gantt-warning, #e6a23c);
}

.task-type-milestone {
  border-left: 3px solid var(--gantt-danger, #f56c6c);
}

/* 任务类型悬停时保持并增强左边框 */
.task-type-story:hover {
  border-left: 5px solid var(--gantt-primary, #409eff);
}

.task-type-task:hover {
  border-left: 5px solid var(--gantt-warning, #e6a23c);
}

.task-type-milestone:hover {
  border-left: 5px solid var(--gantt-danger, #f56c6c);
}

/* 悬停状态下的左边框保持 */
.task-row-hovered.task-type-story {
  border-left: 5px solid var(--gantt-primary, #409eff) !important;
}

.task-row-hovered.task-type-task {
  border-left: 5px solid var(--gantt-warning, #e6a23c) !important;
}

.task-row-hovered.task-type-milestone {
  border-left: 5px solid var(--gantt-danger, #f56c6c) !important;
}

:global(html[data-theme='dark']) .milestone-group-row {
  border-left-color: var(--gantt-danger, #f67c7c);
}

/* 暗黑模式下的任务类型左边框颜色 */
:global(html[data-theme='dark']) .task-type-story {
  border-left-color: var(--gantt-primary, #7db4f0);
}

:global(html[data-theme='dark']) .task-type-task {
  border-left-color: var(--gantt-warning, #f0b83c);
}

:global(html[data-theme='dark']) .task-type-milestone {
  border-left-color: var(--gantt-danger, #f67c7c);
}

.collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  cursor: pointer;
  margin-right: 4px;
  color: var(--gantt-primary);
  border-radius: 2px;
  transition: background-color 0.2s ease;
}

.collapse-btn:hover {
  background-color: var(--gantt-primary-light);
}

.collapse-btn svg {
  transition: transform 0.2s ease;
}

/* 里程碑分组占位空间 - 与折叠按钮对齐 */
.milestone-spacer {
  display: inline-flex;
  width: 18px;
  height: 18px;
  margin-right: 4px;
}

/* 叶子节点占位空间 - 当不显示图标时保持缩进层级 */
.leaf-spacer {
  display: inline-flex;
  width: 20px; /* 18px (折叠按钮宽度) + 2px (额外间距) */
  height: 18px;
}

.task-name-text {
  display: inline-block;
  max-width: calc(100% - 24px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.task-name-text.parent-task {
  font-weight: bold;
  color: var(--gantt-text-parent, var(--gantt-text-primary));
}

.task-icon {
  margin-right: 4px;
  color: var(--gantt-text-muted);
}

.task-icon svg {
  vertical-align: middle;
}

.assignee-info {
  display: flex;
  align-items: center;
  gap: 8px;
}

.avatar {
  min-width: 25px;
  min-height: 25px;
  border-radius: 50%;
  background: var(--gantt-primary);
  color: var(--gantt-text-white);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 500;
  border: 2px solid var(--gantt-border-medium);
  box-sizing: border-box;
}

.assignee-name {
  font-size: 14px;
  color: var(--gantt-text-secondary);
}

.progress-value {
  font-weight: 500;
  color: var(--gantt-text-secondary);
}

.progress-success {
  color: var(--gantt-success);
}

.progress-warning {
  color: var(--gantt-warning);
}

.progress-danger {
  color: var(--gantt-danger);
}

.status-badge {
  display: inline-block;
  padding: 2px 6px;
  border-radius: 2px;
  font-size: 10px;
  font-weight: bold;
  margin-left: 6px;
  color: white;
}

.status-badge.overtime {
  background-color: transparent;
  border: 1px solid var(--gantt-danger);
  color: var(--gantt-danger);
}

.status-badge.overdue {
  background-color: var(--gantt-danger);
}

/* 里程碑分组图标样式 - 统一使用红色并添加发光效果 */
.milestone-group-icon {
  color: var(--gantt-danger, #f56c6c);
  fill: var(--gantt-danger, #f56c6c);
  opacity: 0.9;
  filter: drop-shadow(0 0 6px var(--gantt-danger, #f56c6c));
  animation: milestone-icon-glow 2.5s ease-in-out infinite alternate;
}

/* 里程碑图标悬停效果 */
.task-row:hover .milestone-group-icon {
  filter: drop-shadow(0 0 10px var(--gantt-danger, #f56c6c))
    drop-shadow(0 0 16px rgba(245, 108, 108, 0.4));
  animation: milestone-icon-glow-intense 1.8s ease-in-out infinite alternate;
}

/* 里程碑图标发光动画 */
@keyframes milestone-icon-glow {
  from {
    filter: drop-shadow(0 0 3px var(--gantt-danger, #f56c6c));
  }
  to {
    filter: drop-shadow(0 0 8px var(--gantt-danger, #f56c6c))
      drop-shadow(0 0 12px rgba(245, 108, 108, 0.3));
  }
}

@keyframes milestone-icon-glow-intense {
  from {
    filter: drop-shadow(0 0 8px var(--gantt-danger, #f56c6c))
      drop-shadow(0 0 12px rgba(245, 108, 108, 0.3));
  }
  to {
    filter: drop-shadow(0 0 12px var(--gantt-danger, #f56c6c))
      drop-shadow(0 0 20px rgba(245, 108, 108, 0.5));
  }
}

/* 里程碑行样式 - 统一使用红色 */
.milestone-item-icon {
  color: var(--gantt-danger, #f56c6c);
}

.milestone-empty-col {
  color: var(--gantt-text-disabled, #c0c4cc);
  /* 确保边框颜色与普通数据行一致 */
  border-right-color: var(--gantt-border-light) !important;
}

.milestone-empty-col::after {
  content: '-';
}

/* 暗黑模式适配 */
:global(html[data-theme='dark']) .milestone-row-icon {
  color: var(--gantt-danger, #f67c7c);
}

/* 暗黑模式下的里程碑图标发光效果 */
:global(html[data-theme='dark']) .milestone-group-icon {
  color: var(--gantt-danger, #f67c7c);
  fill: var(--gantt-danger, #f67c7c);
  filter: drop-shadow(0 0 6px var(--gantt-danger, #f67c7c));
  animation: milestone-icon-glow-dark 2.5s ease-in-out infinite alternate;
}

:global(html[data-theme='dark']) .task-row:hover .milestone-group-icon {
  filter: drop-shadow(0 0 10px var(--gantt-danger, #f67c7c))
    drop-shadow(0 0 16px rgba(246, 124, 124, 0.4));
  animation: milestone-icon-glow-intense-dark 1.8s ease-in-out infinite alternate;
}

/* 暗黑模式发光动画 */
@keyframes milestone-icon-glow-dark {
  from {
    filter: drop-shadow(0 0 3px var(--gantt-danger, #f67c7c));
  }
  to {
    filter: drop-shadow(0 0 8px var(--gantt-danger, #f67c7c))
      drop-shadow(0 0 12px rgba(246, 124, 124, 0.3));
  }
}

@keyframes milestone-icon-glow-intense-dark {
  from {
    filter: drop-shadow(0 0 8px var(--gantt-danger, #f67c7c))
      drop-shadow(0 0 12px rgba(246, 124, 124, 0.3));
  }
  to {
    filter: drop-shadow(0 0 12px var(--gantt-danger, #f67c7c))
      drop-shadow(0 0 20px rgba(246, 124, 124, 0.5));
  }
}

:global(html[data-theme='dark']) .milestone-empty-col {
  color: var(--gantt-text-disabled, #606266);
  /* 确保暗黑模式下边框颜色与普通数据行一致 */
  border-right-color: var(--gantt-border-light) !important;
}

/* 暗黑模式的悬停效果 */
:global(html[data-theme='dark']) .task-row:hover {
  box-shadow:
    0 4px 12px rgba(255, 255, 255, 0.1),
    0 2px 8px rgba(0, 0, 0, 0.3);
}

:global(html[data-theme='dark']) .task-row.task-row-hovered {
  background-color: var(--gantt-bg-hover) !important;
  box-shadow:
    0 4px 12px rgba(255, 255, 255, 0.1),
    0 2px 8px rgba(0, 0, 0, 0.3) !important;
}

:global(html[data-theme='dark']) .task-row.parent-task:hover {
  box-shadow:
    0 6px 16px rgba(255, 255, 255, 0.15),
    0 2px 8px rgba(0, 0, 0, 0.4);
}

:global(html[data-theme='dark']) .task-row.parent-task.task-row-hovered {
  background: var(--gantt-bg-hover-parent) !important;
  box-shadow:
    0 6px 16px rgba(255, 255, 255, 0.15),
    0 2px 8px rgba(0, 0, 0, 0.4) !important;
}

:global(html[data-theme='dark']) .milestone-group-row:hover {
  box-shadow:
    0 6px 16px rgba(246, 124, 124, 0.4),
    0 2px 8px rgba(255, 255, 255, 0.1);
}

/* 计时器样式 */
.timer-badge {
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 700;
  margin-left: 8px;
  padding: 1px 6px;
  border-radius: 10px;
  background-color: rgba(0, 0, 0, 0.05);
  color: var(--text-color-secondary);
}

.timer-badge.timer-active {
  color: #e6a23c;
}

.timer-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: #67c23a; /* 绿色 */
  margin-right: 4px;
  animation: pulse 1s infinite;
}

@keyframes pulse {
  0% {
    transform: scale(0.8);
    opacity: 0.8;
  }
  50% {
    transform: scale(1.2);
    opacity: 1;
  }
  100% {
    transform: scale(0.8);
    opacity: 0.8;
  }
}

:global(html[data-theme='dark']) .timer-badge {
  background-color: rgba(255, 255, 255, 0.1);
  color: var(--text-color-secondary-dark);
}

:global(html[data-theme='dark']) .timer-badge.timer-active {
  color: #e6c07b;
}

:global(html[data-theme='dark']) .timer-dot {
  background-color: #85ce61; /* 暗色主题下的绿色 */
}

/* TaskRow拖拽样式 */
.task-row-dragging {
  opacity: 0.6 !important;
  cursor: move !important;
}

/* 拖拽目标高亮样式 */
.task-row-drop-target.drop-after {
  border-bottom: 3px solid var(--gantt-primary, #409eff) !important;
  background-color: rgba(64, 158, 255, 0.05) !important;
}

.task-row-drop-target.drop-child {
  border: 2px solid var(--gantt-primary, #409eff) !important;
  background-color: rgba(64, 158, 255, 0.05) !important;
}

:global(html[data-theme='dark']) .task-row-drop-target.drop-after {
  background-color: rgba(125, 180, 240, 0.1) !important;
}

:global(html[data-theme='dark']) .task-row-drop-target.drop-child {
  background-color: rgba(125, 180, 240, 0.1) !important;
}

/* 声明式列第一列的包装器 */
.first-col-wrapper {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
}

/* 声明式列的 padding，与 header 保持一致 */
.task-row .col {
  padding: 0 10px;
}
</style>
