/* eslint-disable @typescript-eslint/no-explicit-any */
<script setup lang="ts">
import { ref, computed, onUnmounted, onMounted, nextTick, watch } from 'vue'
import type { Task } from '../models/classes/Task'
import { TimelineScale } from '../models/types/TimelineScale'
import TaskContextMenu from './TaskContextMenu.vue'

import { useI18n } from '../composables/useI18n'

interface Props {
  task: Task
  rowHeight: number
  dayWidth: number
  startDate: Date
  isParent?: boolean
  onDoubleClick?: (task: Task) => void
  // 新增：用于粘性文字显示的滚动位置信息
  scrollLeft?: number
  containerWidth?: number
  // 新增：外部控制半圆隐藏状态（用于Timeline初始化等场景）
  hideBubbles?: boolean
  // 新增：时间线数据，用于精确计算subDays定位
  timelineData?: any
  // 新增：当前时间刻度
  currentTimeScale?: TimelineScale
}

const props = defineProps<Props>()

const emit = defineEmits([
  'update:task',
  'bar-mounted',
  'dblclick',
  'drag-end',
  'resize-end',
  'scroll-to-position',
  'start-timer',
  'stop-timer',
  'add-predecessor',
  'add-successor',
  'delete',
  'contextmenu', // 添加原生contextmenu事件声明
])
const { getTranslation } = useI18n()
const t = (key: string): string => {
  return getTranslation(key)
}

// 日期工具函数 - 处理时区安全的日期创建和操作
const createLocalDate = (dateString: string | Date | undefined | null): Date | null => {
  if (!dateString) return null
  if (dateString instanceof Date) {
    return dateString
  }
  if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
    const [year, month, day] = dateString.split('-').map(Number)
    return new Date(year, month - 1, day)
  }
  // 支持带时间的日期字符串 (yyyy-mm-dd hh:mm)
  if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(dateString)) {
    const [datePart, timePart] = dateString.split(' ')
    const [year, month, day] = datePart.split('-').map(Number)
    const [hour, minute] = timePart.split(':').map(Number)
    return new Date(year, month - 1, day, hour, minute)
  }
  const d = new Date(dateString)
  return isNaN(d.getTime()) ? null : d
}

const createLocalToday = (): Date => {
  const now = new Date()
  return new Date(now.getFullYear(), now.getMonth(), now.getDate())
}

const formatDateToLocalString = (date: Date): string => {
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')

  // 在小时视图中，格式化为包含时间的字符串
  if (props.currentTimeScale === TimelineScale.HOUR) {
    const hour = String(date.getHours()).padStart(2, '0')
    const minute = String(date.getMinutes()).padStart(2, '0')
    return `${year}-${month}-${day} ${hour}:${minute}`
  }

  return `${year}-${month}-${day}`
}

const addDaysToLocalDate = (date: Date, days: number): Date => {
  const result = new Date(date)
  result.setDate(result.getDate() + days)
  return result
}

// 新增：小时视图下的时间计算工具函数
const addMinutesToDate = (date: Date, minutes: number): Date => {
  const result = new Date(date)
  result.setMinutes(result.getMinutes() + minutes)
  return result
}

// 新增：计算两个日期之间的分钟差
const getMinutesDiff = (startDate: Date, endDate: Date): number => {
  return Math.round((endDate.getTime() - startDate.getTime()) / (1000 * 60))
}

// 计算是否应该禁用拖拽和调整大小（年度视图下禁用）
const isInteractionDisabled = computed(() => {
  return props.currentTimeScale === TimelineScale.YEAR
})

// 拖拽状态
const isDragging = ref(false)
const isResizingLeft = ref(false)
const isResizingRight = ref(false)
const dragStartX = ref(0)
const dragStartLeft = ref(0)
const dragStartWidth = ref(0)
const resizeStartX = ref(0)
const resizeStartWidth = ref(0)
const resizeStartLeft = ref(0)

// 相对位置拖拽方案：记录鼠标相对于TaskBar的位置
const mouseOffsetX = ref(0) // 鼠标在TaskBar内的相对位置

// 缓存拖拽/拉伸过程中的临时数据，只在鼠标抬起时提交更新
const tempTaskData = ref<{
  startDate?: string
  endDate?: string
} | null>(null)

const barRef = ref<HTMLElement | null>(null)

// 计算任务条位置和宽度
const taskBarStyle = computed(() => {
  const currentStartDate = tempTaskData.value?.startDate || props.task.startDate
  const currentEndDate = tempTaskData.value?.endDate || props.task.endDate

  const startDate = createLocalDate(currentStartDate)
  const endDate = createLocalDate(currentEndDate)
  const baseStart = createLocalDate(props.startDate)
  if (!startDate || !endDate || !baseStart) {
    return {
      left: '0px',
      width: '0px',
      height: `${props.rowHeight - 10}px`,
      top: '4px',
    }
  }

  let left = 0
  let width = 0

  // 小时视图：按分钟精确计算位置（需要考虑时间部分）
  if (props.currentTimeScale === TimelineScale.HOUR) {
    // 确保 baseStart 是当天的 00:00:00
    const baseStartOfDay = new Date(baseStart)
    baseStartOfDay.setHours(0, 0, 0, 0)

    // 处理没有时间部分的日期字符串
    let adjustedStartDate = startDate
    let adjustedEndDate = endDate

    // 检查原始日期字符串是否包含时间部分
    const originalStartStr = currentStartDate || props.task.startDate
    const originalEndStr = currentEndDate || props.task.endDate

    // 如果startDate没有时间部分（格式为YYYY-MM-DD），设置为当日00:00
    if (
      typeof originalStartStr === 'string' &&
      /^\d{4}-\d{2}-\d{2}$/.test(originalStartStr.trim())
    ) {
      adjustedStartDate = new Date(startDate)
      adjustedStartDate.setHours(0, 0, 0, 0)
    }

    // 如果endDate没有时间部分（格式为YYYY-MM-DD），设置为次日00:00
    if (typeof originalEndStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(originalEndStr.trim())) {
      adjustedEndDate = new Date(endDate)
      adjustedEndDate.setDate(adjustedEndDate.getDate() + 1)
      adjustedEndDate.setHours(0, 0, 0, 0)
    }

    // 计算从当天00:00到任务开始和结束的分钟数
    const startMinutes = getMinutesDiff(baseStartOfDay, adjustedStartDate)
    const endMinutes = getMinutesDiff(baseStartOfDay, adjustedEndDate)

    // 每小时40px，每分钟40/60 = 2/3 px
    const pixelPerMinute = 40 / 60

    left = Math.max(0, startMinutes * pixelPerMinute)
    width = Math.max(4, (endMinutes - startMinutes) * pixelPerMinute) // 确保最小4px宽度
  } else {
    // 日视图、周视图、月视图、年视图：只考虑日期部分，忽略时间部分

    // 将日期标准化为当天的00:00:00，忽略时间部分
    const startDateOnly = new Date(
      startDate.getFullYear(),
      startDate.getMonth(),
      startDate.getDate(),
    )
    const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate())
    const baseStartOnly = new Date(
      baseStart.getFullYear(),
      baseStart.getMonth(),
      baseStart.getDate(),
    )

    if (props.currentTimeScale === TimelineScale.YEAR) {
      // 年度视图计算逻辑
      const startPosition = calculateYearViewPosition(startDateOnly, baseStartOnly)
      const endPosition = calculateYearViewPosition(endDateOnly, baseStartOnly)

      left = startPosition
      width = Math.max(endPosition - startPosition, 4) // 确保最小4px宽度
    } else if (
      props.timelineData &&
      props.currentTimeScale &&
      (props.currentTimeScale === TimelineScale.WEEK ||
        props.currentTimeScale === TimelineScale.MONTH ||
        props.currentTimeScale === TimelineScale.QUARTER)
    ) {
      // 优先使用基于timelineData的精确定位（适用于周视图、月视图和季度视图）
      // 计算开始位置
      const startPosition = calculatePositionFromTimelineData(
        startDateOnly,
        props.timelineData,
        props.currentTimeScale,
      )
      // 计算结束位置：为结束日期添加一天来获取正确的结束位置
      const nextDay = new Date(endDateOnly)
      nextDay.setDate(nextDay.getDate() + 1)
      let endPosition = calculatePositionFromTimelineData(
        nextDay,
        props.timelineData,
        props.currentTimeScale,
      )

      // 如果结束日期+1天超出范围，使用结束日期的位置+一天的宽度
      if (endPosition === startPosition) {
        let dayWidth = 60 / 30 // 默认月视图
        if (props.currentTimeScale === TimelineScale.WEEK) {
          dayWidth = 60 / 7
        } else if (props.currentTimeScale === TimelineScale.QUARTER) {
          dayWidth = 60 / 90 // 季度视图：每季度60px，约90天
        }
        endPosition =
          calculatePositionFromTimelineData(
            endDateOnly,
            props.timelineData,
            props.currentTimeScale,
          ) + dayWidth
      }

      left = startPosition
      width = Math.max(endPosition - startPosition, 4) // 确保最小4px宽度
    } else {
      // 日视图：基于日期的简单计算
      const startDiff = Math.floor(
        (startDateOnly.getTime() - baseStartOnly.getTime()) / (1000 * 60 * 60 * 24),
      )

      // 计算持续天数（基于日期，忽略时间）
      const timeDiffMs = endDateOnly.getTime() - startDateOnly.getTime()
      const daysDiff = Math.round(timeDiffMs / (1000 * 60 * 60 * 24))

      // 如果开始和结束是同一天，duration = 1；否则是实际天数差 + 1（包含结束日期）
      const duration = daysDiff === 0 ? 1 : daysDiff + 1

      left = startDiff * props.dayWidth
      width = duration * props.dayWidth
    }
  }

  return {
    left: `${left}px`,
    width: `${width}px`,
    height: `${props.rowHeight - 10}px`,
    top: '4px',
  }
})

// 计算任务状态和颜色
const taskStatus = computed(() => {
  // 父级任务(Story类型)使用与新建按钮一致的配色
  if (props.isParent) {
    return {
      type: 'parent',
      color: '#409eff', // 与新建按钮一致的蓝色
      bgColor: '#409eff',
      borderColor: '#409eff',
    }
  }

  const today = createLocalToday()
  const endDate = createLocalDate(props.task.endDate || '')
  const progress = props.task.progress || 0

  // 已完成
  if (progress >= 100) {
    return {
      type: 'completed',
      color: '#909399', // info color
      bgColor: '#f4f4f5',
      borderColor: '#d3d4d6',
    }
  }

  // 已延迟（结束日期早于今天且未完成）
  if (endDate && endDate < today && progress < 100) {
    return {
      type: 'delayed',
      color: '#f56c6c', // danger color
      bgColor: '#fef0f0',
      borderColor: '#fbc4c4',
    }
  }

  // 进行中（结束日期晚于今天且进度>0）
  if (endDate && endDate >= today && progress > 0) {
    return {
      type: 'in-progress',
      color: '#e6a23c', // warning color
      bgColor: '#fdf6ec',
      borderColor: '#f5dab1',
    }
  }

  // 未开始（进度为0且未延迟）
  return {
    type: 'not-started',
    color: '#409eff', // primary color
    bgColor: '#ecf5ff',
    borderColor: '#b3d8ff',
  }
})

// 判断是否已完成
const isCompleted = computed(() => (props.task.progress || 0) >= 100)

// 计算完成部分的宽度
const progressWidth = computed(() => {
  const progress = props.task.progress || 0
  const totalWidth = parseInt(taskBarStyle.value.width)
  return `${(progress / 100) * totalWidth}px`
})

// 判断是否为周视图（dayWidth小于等于9为周视图）
const isWeekView = computed(() => props.dayWidth <= 9)

// 判断是否为短TaskBar（宽度小于80px）
const isShortTaskBar = computed(() => {
  const width = parseFloat(taskBarStyle.value.width || '0')
  return width < 80
})

// 判断是否需要溢出效果（周视图且短TaskBar）
const needsOverflowEffect = computed(() => isWeekView.value && isShortTaskBar.value)

// 鼠标事件处理 - 使用相对位置拖拽方案
const handleMouseDown = (e: MouseEvent, type: 'drag' | 'resize-left' | 'resize-right') => {
  // 如果已完成或是父级任务或年度视图，禁用所有交互
  if (isCompleted.value || props.isParent || isInteractionDisabled.value) {
    return
  }

  e.preventDefault()
  e.stopPropagation()

  // 清空之前的临时数据
  tempTaskData.value = null

  // 获取TaskBar相对于Timeline容器的位置
  const timelineContainer = document.querySelector('.timeline') as HTMLElement
  if (!timelineContainer || !barRef.value) return

  const barRect = barRef.value.getBoundingClientRect()

  // 计算鼠标相对于TaskBar的位置
  mouseOffsetX.value = e.clientX - barRect.left

  if (type === 'drag') {
    isDragging.value = true
    dragStartX.value = e.clientX
    dragStartLeft.value = parseInt(taskBarStyle.value.left)
    dragStartWidth.value = parseInt(taskBarStyle.value.width)
  } else if (type === 'resize-left') {
    isResizingLeft.value = true
    resizeStartX.value = e.clientX
    resizeStartWidth.value = parseInt(taskBarStyle.value.width)
    resizeStartLeft.value = parseInt(taskBarStyle.value.left)
  } else if (type === 'resize-right') {
    isResizingRight.value = true
    resizeStartX.value = e.clientX
    resizeStartWidth.value = parseInt(taskBarStyle.value.width)
    resizeStartLeft.value = parseInt(taskBarStyle.value.left)
  }

  // 监听自动滚动事件
  window.addEventListener('timeline-auto-scroll', handleAutoScroll as EventListener)

  document.addEventListener('mousemove', handleMouseMove)
  document.addEventListener('mouseup', handleMouseUp)
}

// 处理自动滚动事件
const handleAutoScroll = (event: CustomEvent) => {
  const { scrollDelta } = event.detail

  // 当Timeline滚动时，调整鼠标起始位置以保持相对位置
  if (isDragging.value) {
    dragStartX.value -= scrollDelta
  } else if (isResizingLeft.value || isResizingRight.value) {
    resizeStartX.value -= scrollDelta
  }
}

function reportBarPosition() {
  if (barRef.value) {
    const rect = barRef.value.getBoundingClientRect()
    emit('bar-mounted', {
      id: props.task.id,
      left: rect.left,
      top: rect.top,
      width: rect.width,
      height: rect.height,
    })
  }
}

const handleMouseMove = (e: MouseEvent) => {
  // 发送边界检测事件给Timeline
  window.dispatchEvent(
    new CustomEvent('drag-boundary-check', {
      detail: {
        mouseX: e.clientX,
        isDragging: isDragging.value || isResizingLeft.value || isResizingRight.value,
      },
    }),
  )

  if (isDragging.value) {
    const deltaX = e.clientX - dragStartX.value

    if (props.currentTimeScale === TimelineScale.HOUR) {
      // 小时视图：15分钟刻度对齐
      const pixelPerMinute = 40 / 60 // 每分钟的像素数
      const pixelPer15Minutes = pixelPerMinute * 15 // 15分钟的像素数

      // 计算新的左侧位置，对齐到15分钟刻度
      const newLeftRaw = Math.max(0, dragStartLeft.value + deltaX)
      const newLeft = Math.round(newLeftRaw / pixelPer15Minutes) * pixelPer15Minutes

      // 计算新的开始时间（分钟精度）
      const newStartMinutes = Math.round(newLeft / pixelPerMinute)
      // 确保使用当天的00:00:00作为基准
      const baseStartOfDay = new Date(props.startDate)
      baseStartOfDay.setHours(0, 0, 0, 0)
      const newStartDate = addMinutesToDate(baseStartOfDay, newStartMinutes)

      // 保持任务的持续时间（计算原始任务的时长）
      const originalStartDate = createLocalDate(props.task.startDate) || props.startDate
      const originalEndDate = createLocalDate(props.task.endDate) || props.startDate

      // 如果原始任务是日期格式，转换为当天的时间范围
      let originalDurationMinutes: number
      if (props.task.startDate && !props.task.startDate.includes(' ')) {
        // 纯日期格式，默认按天计算（一天 = 1440 分钟）
        const timeDiffMs = originalEndDate.getTime() - originalStartDate.getTime()
        const daysDiff = Math.max(1, Math.round(timeDiffMs / (1000 * 60 * 60 * 24)) + 1)
        originalDurationMinutes = daysDiff * 24 * 60 // 天数转分钟
      } else {
        // 包含时间格式，按实际时间差计算
        originalDurationMinutes = getMinutesDiff(originalStartDate, originalEndDate)
      }

      const newEndDate = addMinutesToDate(newStartDate, originalDurationMinutes)

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: formatDateToLocalString(newStartDate),
        endDate: formatDateToLocalString(newEndDate),
      }
    } else {
      // 其他视图：保持原有逻辑
      const newLeft = Math.max(0, dragStartLeft.value + deltaX)
      const newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)
      const duration = dragStartWidth.value / props.dayWidth
      const newEndDate = addDaysToLocalDate(newStartDate, duration - 1)

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: formatDateToLocalString(newStartDate),
        endDate: formatDateToLocalString(newEndDate),
      }
    }
  } else if (isResizingLeft.value) {
    const deltaX = e.clientX - resizeStartX.value

    if (props.currentTimeScale === TimelineScale.HOUR) {
      // 小时视图：15分钟刻度对齐
      const pixelPerMinute = 40 / 60
      const pixelPer15Minutes = pixelPerMinute * 15

      // 计算新的左侧位置，对齐到15分钟刻度
      const newLeftRaw = Math.max(0, resizeStartLeft.value + deltaX)
      const newLeft = Math.round(newLeftRaw / pixelPer15Minutes) * pixelPer15Minutes

      // 计算新的开始时间
      const newStartMinutes = Math.round(newLeft / pixelPerMinute)
      // 确保使用当天的00:00:00作为基准
      const baseStartOfDay = new Date(props.startDate)
      baseStartOfDay.setHours(0, 0, 0, 0)
      const newStartDate = addMinutesToDate(baseStartOfDay, newStartMinutes)

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: formatDateToLocalString(newStartDate),
        endDate: props.task.endDate, // 保持原来的结束日期
      }
    } else {
      // 其他视图：保持原有逻辑
      const newLeft = Math.max(0, resizeStartLeft.value + deltaX)
      const newStartDate = addDaysToLocalDate(props.startDate, newLeft / props.dayWidth)

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: formatDateToLocalString(newStartDate),
        endDate: props.task.endDate, // 保持原来的结束日期
      }
    }
  } else if (isResizingRight.value) {
    const deltaX = e.clientX - resizeStartX.value

    if (props.currentTimeScale === TimelineScale.HOUR) {
      // 小时视图：15分钟刻度对齐
      const pixelPerMinute = 40 / 60
      const pixelPer15Minutes = pixelPerMinute * 15

      // 计算新的宽度，对齐到15分钟刻度
      const newWidthRaw = Math.max(pixelPer15Minutes, resizeStartWidth.value + deltaX)
      const newWidth = Math.round(newWidthRaw / pixelPer15Minutes) * pixelPer15Minutes

      // 计算新的持续时间（分钟）
      const newDurationMinutes = Math.round(newWidth / pixelPerMinute)

      // 计算新的结束时间
      const originalStartDate = createLocalDate(props.task.startDate) || props.startDate
      const newEndDate = addMinutesToDate(originalStartDate, newDurationMinutes)

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: props.task.startDate, // 保持原来的开始日期
        endDate: formatDateToLocalString(newEndDate),
      }
    } else {
      // 其他视图：保持原有逻辑
      const newWidth = Math.max(props.dayWidth, resizeStartWidth.value + deltaX)
      const newDurationDays = newWidth / props.dayWidth
      const newEndDate = addDaysToLocalDate(
        props.startDate,
        resizeStartLeft.value / props.dayWidth + newDurationDays - 1,
      )

      // 只更新临时数据，不触发事件
      tempTaskData.value = {
        startDate: props.task.startDate, // 保持原来的开始日期
        endDate: formatDateToLocalString(newEndDate),
      }
    }
  }
}

const handleMouseUp = () => {
  // 停止边界检测
  window.dispatchEvent(
    new CustomEvent('drag-boundary-check', {
      detail: {
        mouseX: 0,
        isDragging: false,
      },
    }),
  )

  // 如果有临时数据，说明发生了拖拽或拉伸，提交数据更新
  if (tempTaskData.value) {
    const updatedTask = {
      ...props.task,
      ...tempTaskData.value,
    }

    // 判断是拖拽还是拉伸
    if (isDragging.value) {
      emit('drag-end', updatedTask)
    } else if (isResizingLeft.value || isResizingRight.value) {
      emit('resize-end', updatedTask)
    }
    emit('update:task', updatedTask)

    // 清空临时数据
    tempTaskData.value = null

    // 下一帧报告新位置
    nextTick(() => {
      reportBarPosition()
    })
  }

  // 清理自动滚动监听器
  window.removeEventListener('timeline-auto-scroll', handleAutoScroll as EventListener)

  isDragging.value = false
  isResizingLeft.value = false
  isResizingRight.value = false
  document.removeEventListener('mousemove', handleMouseMove)
  document.removeEventListener('mouseup', handleMouseUp)
}

onMounted(() => {
  nextTick(() => {
    reportBarPosition()
  })

  // 监听时间刻度变化事件，重新计算位置
  const handleTimelineScaleUpdate = () => {
    nextTick(() => {
      reportBarPosition()
    })
  }

  // 监听强制重新计算事件
  const handleForceRecalculate = () => {
    // 延迟稍长一点，确保DOM完全更新
    nextTick(() => {
      setTimeout(() => {
        reportBarPosition()
      }, 10)
    })
  }

  window.addEventListener('timeline-scale-updated', handleTimelineScaleUpdate)
  window.addEventListener('timeline-force-recalculate', handleForceRecalculate)

  // 监听全局关闭菜单事件
  window.addEventListener('close-all-taskbar-menus', closeContextMenu)

  // 清理函数
  onUnmounted(() => {
    window.removeEventListener('timeline-scale-updated', handleTimelineScaleUpdate)
    window.removeEventListener('timeline-force-recalculate', handleForceRecalculate)
    window.removeEventListener('close-all-taskbar-menus', closeContextMenu)
  })
})

// 监听任务数据变化，重新报告位置
watch(
  () => [props.task.startDate, props.task.endDate],
  () => {
    nextTick(() => {
      reportBarPosition()
    })
  },
  { deep: true },
)

// 处理TaskBar双击事件
const handleTaskBarDoubleClick = (e: MouseEvent) => {
  // 阻止事件冒泡，避免触发拖拽等其他事件
  e.stopPropagation()
  e.preventDefault()

  // 清理任何可能残留的拖拽状态和临时数据
  isDragging.value = false
  isResizingLeft.value = false
  isResizingRight.value = false
  tempTaskData.value = null

  // 移除可能残留的事件监听器
  document.removeEventListener('mousemove', handleMouseMove)
  document.removeEventListener('mouseup', handleMouseUp)

  // 优先调用外部传入的双击处理器
  if (props.onDoubleClick && typeof props.onDoubleClick === 'function') {
    props.onDoubleClick(props.task)
  } else {
    // 默认行为：发出双击事件给父组件
    emit('dblclick', props.task)
  }
}

// 计算粘性样式 - 支持左右边界的精细控制
const stickyStyles = computed(() => {
  const scrollLeft = props.scrollLeft || 0
  const containerWidth = props.containerWidth || 0

  if (!scrollLeft && !containerWidth) {
    return {
      nameLeft: '',
      namePosition: '',
      nameTop: '',
      progressLeft: '',
      progressPosition: '',
      progressTop: '',
    }
  }

  const taskLeft = parseInt(taskBarStyle.value.left)
  const taskWidth = parseInt(taskBarStyle.value.width)
  const taskRight = taskLeft + taskWidth
  const taskCenterX = taskLeft + taskWidth / 2
  const leftBoundary = scrollLeft
  const rightBoundary = scrollLeft + containerWidth

  // 默认样式
  let nameLeft = ''
  let namePosition = ''
  let nameTop = ''
  let progressLeft = ''
  let progressPosition = ''
  let progressTop = ''

  // 估算文字内容的实际位置
  const nameText = props.task.name || ''
  const nameWidth = Math.max(nameText.length * 7, 40)
  const progressWidth = 35

  // 计算名称和进度在默认居中状态下的位置
  const nameLeftPos = taskCenterX - nameWidth / 2
  const nameRightPos = taskCenterX + nameWidth / 2
  const progressLeftPos = taskCenterX - progressWidth / 2
  const progressRightPos = taskCenterX + progressWidth / 2

  // 左侧边界粘性逻辑
  const nameNeedsLeftSticky =
    nameLeftPos < leftBoundary && taskRight > leftBoundary && taskCenterX < leftBoundary

  // 右侧边界粘性逻辑
  const nameNeedsRightSticky =
    nameRightPos > rightBoundary && taskLeft < rightBoundary && taskCenterX > rightBoundary

  // 名称粘性处理
  if (nameNeedsLeftSticky) {
    const offset = leftBoundary - taskLeft
    nameLeft = `${offset + 8}px`
    namePosition = 'absolute'
    nameTop = '6px'
  } else if (nameNeedsRightSticky) {
    const offset = rightBoundary - taskLeft - nameWidth
    nameLeft = `${offset - 8}px`
    namePosition = 'absolute'
    nameTop = '6px'
  }

  // 进度左侧边界粘性逻辑
  const progressNeedsLeftSticky =
    progressLeftPos < leftBoundary && taskRight > leftBoundary && taskCenterX < leftBoundary

  // 进度右侧边界粘性逻辑
  const progressNeedsRightSticky =
    progressRightPos > rightBoundary && taskLeft < rightBoundary && taskCenterX > rightBoundary

  // 进度粘性处理
  if (progressNeedsLeftSticky) {
    const offset = leftBoundary - taskLeft
    progressLeft = `${offset + 8}px`
    progressPosition = 'absolute'
    progressTop = '24px'
  } else if (progressNeedsRightSticky) {
    const offset = rightBoundary - taskLeft - progressWidth
    progressLeft = `${offset - 8}px`
    progressPosition = 'absolute'
    progressTop = '24px'
  }

  return {
    nameLeft,
    namePosition,
    nameTop,
    progressLeft,
    progressPosition,
    progressTop,
  }
})

// 计算气泡指示器的显示状态和位置
const bubbleIndicator = computed(() => {
  const scrollLeft = props.scrollLeft || 0
  const containerWidth = props.containerWidth || 0

  // 如果没有有效的滚动信息，不显示气泡
  if (!containerWidth || containerWidth <= 0) {
    return {
      show: false,
      left: '0px',
      side: 'left',
      color: '#409eff',
      animationType: 'none',
    }
  }

  // 如果正在初始化、强制隐藏状态或外部要求隐藏，不显示气泡
  if (isInitializing.value || bubbleHidden.value || props.hideBubbles) {
    return {
      show: false,
      left: '0px',
      side: 'left',
      color: taskStatus.value.color,
      animationType: 'none',
    }
  }

  // 获取实际的DOM位置（考虑缩放等因素）
  const taskLeft = parseInt(taskBarStyle.value.left)
  const taskWidth = parseInt(taskBarStyle.value.width)
  const taskRight = taskLeft + taskWidth
  const leftBoundary = scrollLeft
  const rightBoundary = scrollLeft + containerWidth

  // 检查边界状态
  const isCompletelyOutOfLeft = taskRight <= leftBoundary
  const isCompletelyOutOfRight = taskLeft >= rightBoundary

  // 只有完全超出边界时才显示半圆
  if (isCompletelyOutOfLeft) {
    return {
      show: true,
      left: `${leftBoundary - taskLeft - 4}px`, // 圆心在边界上，向左偏移半径(4px)
      side: 'left',
      color: taskStatus.value.color,
      animationType: 'morphToSemiCircle',
    }
  }

  if (isCompletelyOutOfRight) {
    return {
      show: true,
      left: `${rightBoundary - taskLeft - 8}px`, // 右侧半圆位置调整，减少偏移量
      side: 'right',
      color: taskStatus.value.color,
      animationType: 'morphToSemiCircle',
    }
  }

  // 部分可见或完全可见时不显示
  return {
    show: false,
    left: '0px',
    side: 'left',
    color: taskStatus.value.color,
    animationType: 'none',
  }
})

// 气泡 tooltip 状态
const showTooltip = ref(false)
const tooltipPosition = ref({ x: 0, y: 0 })

// 跟踪滚动状态，避免非滚动时的动画
const isScrollingContext = ref(false)
const scrollTimeout = ref<number | null>(null)
const hasManualResize = ref(false) // 跟踪是否有手动resize事件
const isInitializing = ref(true) // 跟踪初始化状态
const isAutoScrolling = ref(false) // 跟踪自动滚动状态（如定位到今日、点击半圆定位等）
const bubbleHidden = ref(false) // 控制半圆的强制隐藏状态

// 气泡点击事件 - 将TaskBar定位到Timeline中间
const handleBubbleClick = () => {
  const scrollLeft = props.scrollLeft || 0
  const containerWidth = props.containerWidth || 0

  if (!scrollLeft && !containerWidth) return

  const taskLeft = parseInt(taskBarStyle.value.left)
  const taskWidth = parseInt(taskBarStyle.value.width)
  const taskCenterX = taskLeft + taskWidth / 2

  // 计算将TaskBar中心定位到Timeline中心所需的滚动位置
  const targetScrollLeft = taskCenterX - containerWidth / 2

  // 标记为自动滚动状态，隐藏所有半圆
  isAutoScrolling.value = true
  bubbleHidden.value = true

  // 立即隐藏tooltip
  showTooltip.value = false

  // 通过事件向Timeline发送滚动请求
  emit('scroll-to-position', targetScrollLeft)
}

// 监听滚动相关的props变化，判断是否在滚动
watch(
  () => [props.scrollLeft, props.containerWidth],
  (newValues, oldValues) => {
    // 检查是否是真实的滚动变化（而非初始化或resize）
    const [newScrollLeft, newContainerWidth] = newValues
    const [oldScrollLeft, oldContainerWidth] = oldValues || [0, 0]

    // 确保数值有效
    const safeNewScrollLeft = newScrollLeft || 0
    const safeNewContainerWidth = newContainerWidth || 0
    const safeOldScrollLeft = oldScrollLeft || 0
    const safeOldContainerWidth = oldContainerWidth || 0

    // 如果容器宽度发生变化（包括Splitter拖拽、TaskList展开收起、窗口resize等）
    if (Math.abs(safeNewContainerWidth - safeOldContainerWidth) > 1 && safeOldContainerWidth > 0) {
      hasManualResize.value = true

      // 容器宽度变化时，强制重新计算气泡显示状态
      // 立即触发bubbleIndicator重新计算
      nextTick(() => {
        // computed会自动重新计算
      })

      // 延长禁用动画的时间，确保各种resize操作稳定
      setTimeout(() => {
        hasManualResize.value = false
      }, 500) // 给容器变化留足够时间稳定
      return
    }

    // 首次接收到有效的滚动数据，标记初始化完成
    if (isInitializing.value && safeNewScrollLeft >= 0 && safeNewContainerWidth > 0) {
      // 延迟标记初始化完成，等待初始滚动动画结束
      setTimeout(() => {
        isInitializing.value = false
      }, 1000) // 给初始化滚动留足够时间
    }

    // 只有在scrollLeft变化且没有resize时才认为是滚动
    if (safeNewScrollLeft !== safeOldScrollLeft && !hasManualResize.value) {
      isScrollingContext.value = true

      // 清除之前的超时
      if (scrollTimeout.value) {
        clearTimeout(scrollTimeout.value)
      }

      // 滚动结束后的处理
      scrollTimeout.value = setTimeout(() => {
        isScrollingContext.value = false

        // 如果是自动滚动结束，恢复半圆显示
        if (isAutoScrolling.value) {
          isAutoScrolling.value = false
          // 延迟一点时间再显示半圆，确保滚动完全停止
          setTimeout(() => {
            bubbleHidden.value = false
          }, 300)
        }
      }, 200)
    }
  },
)

// 监听外部hideBubbles属性变化，确保Timeline的容器变化能及时反应
watch(
  () => props.hideBubbles,
  (newHidden, oldHidden) => {
    // 当Timeline设置hideBubbles从true变为false时，强制重新计算半圆状态
    if (oldHidden && !newHidden) {
      nextTick(() => {
        // 强制重新计算bubbleIndicator，确保容器宽度变化后正确显示半圆
      })
    }
  },
)

// 监听TaskBar可见性变化，只在滚动时实现重新出现动画
watch(
  () => bubbleIndicator.value.show,
  () => {
    // TaskBar重新出现时，不需要动画效果
    // 半圆会自然消失，TaskBar会立即显示
  },
)

// 监听页面缩放和大小变化，重新计算气泡位置
const handleResize = () => {
  // timeline区域resize时，立即重新计算半圆显示状态
  hasManualResize.value = true

  // 强制重新计算bubbleIndicator
  nextTick(() => {
    // computed会自动重新计算
  })

  // 短时间后恢复正常状态，允许动画
  setTimeout(() => {
    hasManualResize.value = false
  }, 300) // 缩短时间，快速恢复
}

onMounted(() => {
  // 监听窗口大小变化和缩放
  window.addEventListener('resize', handleResize)
  window.addEventListener('zoom', handleResize) // 某些浏览器支持
})

onUnmounted(() => {
  document.removeEventListener('mousemove', handleMouseMove)
  document.removeEventListener('mouseup', handleMouseUp)
  window.removeEventListener('resize', handleResize)
  window.removeEventListener('zoom', handleResize)
})

// 处理气泡悬停
const handleBubbleMouseEnter = (event: MouseEvent) => {
  showTooltip.value = true

  // 智能定位：右侧气泡在左侧显示tooltip，左侧气泡在右侧显示
  const isRightBubble = bubbleIndicator.value.side === 'right'
  const offsetX = isRightBubble ? -180 : 15 // 右侧气泡向左偏移距离调整，与左侧距离一致

  tooltipPosition.value = {
    x: event.clientX + offsetX,
    y: event.clientY - 10,
  }
}

const handleBubbleMouseLeave = () => {
  showTooltip.value = false
}

// 处理气泡点击事件 - 点击时隐藏tooltip，但不影响定位功能
const handleBubbleMouseDown = (event: MouseEvent) => {
  // 阻止mousedown事件冒泡，防止影响其他功能
  event.stopPropagation()
  // 点击时隐藏tooltip
  showTooltip.value = false
}

// 格式化日期显示
const formatDisplayDate = (dateStr: string | undefined): string => {
  if (!dateStr) return t('dateNotSet')
  const date = createLocalDate(dateStr)
  if (!date) return t('dateNotSet')

  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  return `${year}-${month}-${day}`
}

// 计算工时信息
const workHourInfo = computed(() => {
  // 这里可以根据实际的数据结构调整
  const startDate = createLocalDate(props.task.startDate)
  const endDate = createLocalDate(props.task.endDate)

  let totalHours = 0
  if (startDate && endDate) {
    const diffTime = Math.abs(endDate.getTime() - startDate.getTime())
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
    totalHours = diffDays * 8 // 假设每天8小时
  }

  const progress = props.task.progress || 0
  const usedHours = Math.round((totalHours * progress) / 100)

  return {
    total: totalHours,
    used: usedHours,
  }
})

// 判断是否应该显示进度百分比
const shouldShowProgress = computed(() => {
  // 任何情况下都显示完成率（包括0%和undefined），确保始终展示
  return true
})

// Helper functions to create type-safe style objects
const getNameStyles = () => {
  const styles = stickyStyles.value
  const result: Record<string, string> = {}

  if (styles.nameLeft) result.left = styles.nameLeft
  if (styles.namePosition) result.position = styles.namePosition
  if (styles.nameTop) result.top = styles.nameTop

  return result
}

const getProgressStyles = () => {
  const styles = stickyStyles.value
  const result: Record<string, string> = {}

  if (styles.progressLeft) result.left = styles.progressLeft
  if (styles.progressPosition) result.position = styles.progressPosition
  if (styles.progressTop) result.top = styles.progressTop

  return result
}

// 年度视图位置计算函数
const calculateYearViewPosition = (targetDate: Date, baseStartDate: Date): number => {
  const targetYear = targetDate.getFullYear()
  const baseYear = baseStartDate.getFullYear()

  // 每年的宽度是360px，每半年180px
  const yearWidth = 360
  const halfYearWidth = 180

  // 计算目标年份相对于基准年份的偏移
  const yearOffset = targetYear - baseYear
  let position = yearOffset * yearWidth

  // 判断是上半年还是下半年
  const month = targetDate.getMonth() + 1 // getMonth()返回0-11，需要+1

  if (month > 6) {
    // 下半年，添加半年偏移
    position += halfYearWidth
  }

  // 在半年内的具体位置计算
  let dayOffset = 0
  let startOfHalfYear: Date

  if (month <= 6) {
    // 上半年：1-6月
    startOfHalfYear = new Date(targetYear, 0, 1) // 1月1日
  } else {
    // 下半年：7-12月
    startOfHalfYear = new Date(targetYear, 6, 1) // 7月1日
  }

  dayOffset = Math.floor((targetDate.getTime() - startOfHalfYear.getTime()) / (1000 * 60 * 60 * 24))

  // 半年大约181-184天，将天数映射到180px的宽度
  const daysInHalfYear =
    month <= 6
      ? Math.floor(
        (new Date(targetYear, 6, 1).getTime() - new Date(targetYear, 0, 1).getTime()) /
            (1000 * 60 * 60 * 24),
      )
      : Math.floor(
        (new Date(targetYear + 1, 0, 1).getTime() - new Date(targetYear, 6, 1).getTime()) /
            (1000 * 60 * 60 * 24),
      )

  const dayPositionInHalfYear = (dayOffset / daysInHalfYear) * halfYearWidth
  position += dayPositionInHalfYear

  return position
}

// 基于timelineData和subDays精确计算日期位置的函数
const calculatePositionFromTimelineData = (
  targetDate: Date,
  timelineData: Array<{
    year: number
    month: number
    startDate: Date
    endDate: Date
    subDays?: Array<{ date: Date; dayOfWeek?: number }>
    monthData?: { dayCount: number }
    weeks?: Array<{
      weekStart: Date
      weekEnd: Date
      subDays: Array<{ date: Date; dayOfWeek?: number }>
    }>
  }>,
  timeScale: TimelineScale,
) => {
  let cumulativePosition = 0

  for (const periodData of timelineData) {
    if (timeScale === TimelineScale.QUARTER) {
      // 季度视图：处理quarters结构
      const quarters = ((periodData as Record<string, unknown>).quarters as unknown[]) || []

      for (const quarter of quarters) {
        const quarterObj = quarter as Record<string, unknown>
        const quarterStart = new Date(quarterObj.startDate as string)
        const quarterEnd = new Date(quarterObj.endDate as string)

        if (targetDate >= quarterStart && targetDate <= quarterEnd) {
          // 找到目标日期所在的季度
          const quarterWidth = 60
          const daysInQuarter = Math.ceil(
            (quarterEnd.getTime() - quarterStart.getTime()) / (1000 * 60 * 60 * 24),
          )
          const dayWidth = quarterWidth / daysInQuarter
          const dayInQuarter = Math.ceil(
            (targetDate.getTime() - quarterStart.getTime()) / (1000 * 60 * 60 * 24),
          )
          return cumulativePosition + dayInQuarter * dayWidth
        }

        // 累加每季度的宽度
        cumulativePosition += 60
      }
    } else if (timeScale === TimelineScale.WEEK) {
      // 周视图：处理嵌套的weeks结构
      const weeks = periodData.weeks || []

      for (const week of weeks) {
        const weekStart = new Date(week.weekStart)
        const weekEnd = new Date(week.weekEnd)

        if (targetDate >= weekStart && targetDate <= weekEnd) {
          // 找到目标日期所在的周
          const weekWidth = 60
          const subDays = week.subDays || []
          const dayWidth = weekWidth / 7

          // 在subDays中查找目标日期的位置
          for (let i = 0; i < subDays.length; i++) {
            const subDay = subDays[i]
            const subDayDate = new Date(subDay.date)
            // 比较日期（忽略时分秒）
            if (
              subDayDate.getFullYear() === targetDate.getFullYear() &&
              subDayDate.getMonth() === targetDate.getMonth() &&
              subDayDate.getDate() === targetDate.getDate()
            ) {
              return cumulativePosition + i * dayWidth
            }
          }

          // 如果没找到精确匹配，回退到dayOfWeek计算
          const dayOfWeek = targetDate.getDay()
          return cumulativePosition + dayOfWeek * dayWidth
        }

        // 累加每周的宽度
        cumulativePosition += 60
      }
    } else if (timeScale === TimelineScale.MONTH) {
      // 月视图：处理扁平化的subDays结构
      const periodStart = new Date(periodData.startDate)
      const periodEnd = new Date(periodData.endDate)

      if (targetDate >= periodStart && targetDate <= periodEnd) {
        // 找到目标日期所在的时间段
        const monthWidth = 60
        const daysInMonth = periodData.monthData?.dayCount || 30
        const dayWidth = monthWidth / daysInMonth
        const dayInMonth = targetDate.getDate()
        return cumulativePosition + (dayInMonth - 1) * dayWidth
      }

      // 累加每月的宽度
      cumulativePosition += 60
    }
  }

  return cumulativePosition // 如果没找到，返回累计位置
}

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

function handleContextMenu(event: MouseEvent) {
  // 先广播关闭所有TaskBar菜单
  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()
}

// 监听全局关闭菜单事件
onMounted(() => {
  window.addEventListener('close-all-taskbar-menus', closeContextMenu)
})
onUnmounted(() => {
  window.removeEventListener('close-all-taskbar-menus', closeContextMenu)
})
</script>

<template>
  <div
    ref="barRef"
    class="task-bar"
    :style="{
      ...taskBarStyle,
      backgroundColor: taskStatus.bgColor,
      borderColor: taskStatus.borderColor,
      color: taskStatus.color,
      cursor: isCompleted || isParent ? 'default' : 'move',
      '--row-height': `${rowHeight}px` /* 传递行高给CSS变量 */,
    }"
    :class="{
      dragging: isDragging,
      resizing: isResizingLeft || isResizingRight,
      completed: isCompleted,
      'parent-task': isParent,
      'week-view': isWeekView,
      'short-task-bar': isShortTaskBar,
      'overflow-effect': needsOverflowEffect,
    }"
    @contextmenu="handleContextMenu"
    @dblclick="handleTaskBarDoubleClick"
  >
    <!-- 父级任务的标签 -->
    <div v-if="isParent" class="parent-label">{{ task.name }} ({{ task.progress || 0 }}%)</div>

    <!-- 完成进度条（非父级任务） -->
    <div
      v-if="!isParent && task.progress && task.progress > 0"
      class="progress-bar"
      :style="{
        width: progressWidth,
        backgroundColor: taskStatus.color,
      }"
    ></div>

    <!-- 左侧调整把手 -->
    <div
      v-if="!isCompleted && !isParent && !isInteractionDisabled"
      class="resize-handle resize-handle-left"
      @mousedown="e => handleMouseDown(e, 'resize-left')"
    ></div>

    <!-- 任务条主体（非父级任务） -->
    <div
      v-if="!isParent"
      class="task-bar-content"
      :style="{ cursor: isInteractionDisabled ? 'default' : 'move' }"
      @mousedown="e => (isInteractionDisabled ? null : handleMouseDown(e, 'drag'))"
    >
      <!-- 任务名称 -->
      <div class="task-name" :style="getNameStyles()">
        {{ task.name }}
      </div>

      <!-- 进度百分比 -->
      <div v-if="shouldShowProgress" class="task-progress" :style="getProgressStyles()">
        {{ task.progress || 0 }}%
      </div>
    </div>

    <!-- 右侧调整把手 -->
    <div
      v-if="!isCompleted && !isParent && !isInteractionDisabled"
      class="resize-handle resize-handle-right"
      @mousedown="e => handleMouseDown(e, 'resize-right')"
    ></div>

    <!-- 半圆气泡指示器 - 只在 TaskBar 完全消失时显示 -->
    <div
      v-if="bubbleIndicator.show && !isParent"
      class="bubble-indicator"
      :class="[
        `bubble-${bubbleIndicator.side}`,
        `bubble-animation-${bubbleIndicator.animationType}`,
      ]"
      :style="{
        left: bubbleIndicator.left,
        backgroundColor: bubbleIndicator.color,
        borderColor: bubbleIndicator.color,
      }"
      @mouseenter="handleBubbleMouseEnter"
      @mouseleave="handleBubbleMouseLeave"
      @mousedown="handleBubbleMouseDown"
      @click="handleBubbleClick"
    ></div>

    <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>

  <!-- Tooltip 弹窗 -->
  <Teleport to="body">
    <div
      v-if="showTooltip"
      class="task-tooltip"
      :style="{
        left: `${tooltipPosition.x}px`,
        top: `${tooltipPosition.y}px`,
      }"
    >
      <div class="tooltip-arrow"></div>
      <div class="tooltip-title">{{ task.name }}</div>
      <div class="tooltip-content">
        <div class="tooltip-row">
          <span class="tooltip-label"> {{ t('startDate') }}:</span>
          <span class="tooltip-value">{{ formatDisplayDate(task.startDate) }}</span>
        </div>
        <div class="tooltip-row">
          <span class="tooltip-label">{{ t('endDate') }}:</span>
          <span class="tooltip-value">{{ formatDisplayDate(task.endDate) }}</span>
        </div>
        <div class="tooltip-row">
          <span class="tooltip-label">{{ t('estimatedHours') }}:</span>
          <span class="tooltip-value">{{ workHourInfo.total }}h</span>
        </div>
        <div class="tooltip-row">
          <span class="tooltip-label"> {{ t('actualHours') }}:</span>
          <span class="tooltip-value">{{ workHourInfo.used }}h</span>
        </div>
        <div class="tooltip-row">
          <span class="tooltip-label"> {{ t('progress') }}:</span>
          <span class="tooltip-value">{{ task.progress || 0 }}%</span>
        </div>
      </div>
    </div>
  </Teleport>
</template>

<style scoped>
.task-bar {
  position: absolute;
  border-radius: 4px;
  user-select: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  transition: box-shadow 0.2s;
  /*min-width: 60px;*/
  z-index: 100;
  border: 2px solid;
  overflow: visible; /* 允许内容超出 TaskBar */
}

.task-bar:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  cursor: pointer;
}

.task-bar.completed {
  cursor: pointer !important;
}

.task-bar.completed:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  cursor: pointer;
}

.task-bar.dragging {
  opacity: 0.8;
  z-index: 1000;
}

.task-bar.resizing {
  z-index: 1000;
}

.task-bar.parent-task {
  position: relative;
  border-radius: 0; /* 移除圆角，使用线性设计 */
  margin-bottom: 20px; /* 为标签和垂直线留出空间 */
  height: 10px !important; /* 降低高度，让条更细 */
  border: none; /* 移除边框 */
  background: #409eff !important; /* 与新建按钮一致的蓝色 */
  box-shadow: none; /* 移除阴影 */
  top: 50% !important; /* 上下居中 */
  transform: translateY(-50%); /* 上下居中 */
  cursor: pointer !important; /* 允许双击编辑 */
  overflow: visible; /* 确保伪元素可见 */
}

/* 父级任务的标签 */
.task-bar.parent-task .parent-label {
  position: absolute;
  top: -8px;
  left: 50%;
  transform: translateX(-50%);
  background: #409eff; /* 与新建按钮一致的蓝色 */
  color: white;
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  z-index: 20;
}

/* 左侧向下箭头 - 更尖 */
.task-bar.parent-task::before {
  content: '';
  position: absolute;
  top: 10px; /* 位于进度条下方 */
  left: 0;
  width: 0;
  height: 0;
  border-right: 6px solid transparent; /* 减小宽度，让箭头更尖 */
  border-top: 10px solid #409eff; /* 与新建按钮一致的蓝色 */
  z-index: 15;
}

/* 右侧向下箭头 - 更尖 */
.task-bar.parent-task::after {
  content: '';
  position: absolute;
  top: 10px; /* 位于进度条下方 */
  right: 0;
  width: 0;
  height: 0;
  border-left: 6px solid transparent; /* 减小宽度，让箭头更尖 */
  border-top: 10px solid #409eff; /* 与新建按钮一致的蓝色 */
  z-index: 15;
}

.progress-bar {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  opacity: 0.3;
  transition: width 0.3s ease;
}

.task-bar-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 0 8px;
  font-size: 12px;
  font-weight: 500;
  text-align: center;
  overflow: visible; /* 允许内容超出 */
  position: relative;
  z-index: 1;
}

.task-name {
  white-space: nowrap;
  overflow: visible;
  line-height: 1.2;
  font-size: 12px;
  font-weight: 700; /* 加粗显示 */
  z-index: 10;
  /* 移除背景样式，保持原始状态 */
}

.task-progress {
  opacity: 0.9;
  font-size: 11px;
  font-weight: 700; /* 加粗显示 */
  z-index: 10;
  /* 移除背景样式，保持原始状态 */
}

.resize-handle {
  position: absolute;
  top: 0;
  width: 6px;
  height: 100%;
  cursor: ew-resize;
  background: rgba(0, 0, 0, 0.1);
  border-radius: 2px;
  transition: background 0.2s;
  z-index: 2;
}

.resize-handle:hover {
  background: rgba(0, 0, 0, 0.2);
}

.resize-handle-left {
  left: 0;
}

.resize-handle-right {
  right: 0;
}

/* 溢出效果下的拉伸handle优化 */
.task-bar.overflow-effect .resize-handle {
  z-index: 20; /* 确保handle在溢出内容之上 */
  background: rgba(0, 0, 0, 0.15); /* 稍微加深以提高可见性 */
}

.task-bar.overflow-effect .resize-handle:hover {
  background: rgba(0, 0, 0, 0.3);
  width: 8px; /* 悬停时稍微加宽 */
}

/* 溢出模式下左右handle的位置调整 */
.task-bar.overflow-effect .resize-handle-left {
  left: 0;
}

.task-bar.overflow-effect .resize-handle-right {
  right: 0;
}

/* === 半圆气泡指示器样式 === */
.bubble-indicator {
  position: absolute;
  top: 50%;
  width: 8px; /* 半圆宽度 */
  height: 16px; /* 半圆高度 */
  z-index: 15;
  cursor: pointer;
  border: 2px solid;
  transform: translateY(-50%);
  box-shadow:
    0 2px 8px rgba(0, 0, 0, 0.15),
    0 1px 3px rgba(0, 0, 0, 0.3);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

/* 左侧半圆 - 圆心在边界上，只显示右半部分 */
.bubble-left {
  border-radius: 0 8px 8px 0;
  border-left: none;
  transform: translateY(-50%); /* 不需要额外偏移，圆心已在边界 */
}

/* 右侧半圆 - 圆心在边界上，只显示左半部分 */
.bubble-right {
  border-radius: 8px 0 0 8px;
  border-right: none;
  transform: translateY(-50%); /* 不需要额外偏移，圆心已在边界 */
}

/* 悬停效果 */
.bubble-indicator:hover {
  transform: translateY(-50%) scale(1.2);
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.2),
    0 2px 6px rgba(0, 0, 0, 0.4);
}

.bubble-left:hover {
  transform: translateY(-50%) scale(1.2);
}

.bubble-right:hover {
  transform: translateY(-50%) scale(1.2);
}

/* === 半圆气泡动画效果 === */

/* TaskBar 边缘变成左侧半圆的动画 */
@keyframes morphToLeftSemiCircle {
  0% {
    width: 60px;
    height: 30px;
    border-radius: 4px 0 0 4px;
    border-right: 2px solid;
    border-left: none;
    opacity: 0.8;
    transform: translateY(-50%);
  }

  30% {
    width: 30px;
    height: 28px;
    border-radius: 6px 0 0 6px;
    opacity: 0.9;
    transform: translateY(-50%);
  }

  70% {
    width: 12px;
    height: 20px;
    border-radius: 0 10px 10px 0;
    border-right: 2px solid;
    border-left: none;
    opacity: 1;
    transform: translateY(-50%);
  }

  100% {
    width: 8px;
    height: 16px;
    border-radius: 0 8px 8px 0;
    border-right: 2px solid;
    border-left: none;
    opacity: 1;
    transform: translateY(-50%);
  }
}

/* TaskBar 边缘变成右侧半圆的动画 */
@keyframes morphToRightSemiCircle {
  0% {
    width: 60px;
    height: 30px;
    border-radius: 0 4px 4px 0;
    border-left: 2px solid;
    border-right: none;
    opacity: 0.8;
    transform: translateY(-50%);
  }

  30% {
    width: 30px;
    height: 28px;
    border-radius: 0 6px 6px 0;
    opacity: 0.9;
    transform: translateY(-50%);
  }

  70% {
    width: 12px;
    height: 20px;
    border-radius: 10px 0 0 10px;
    border-left: 2px solid;
    border-right: none;
    opacity: 1;
    transform: translateY(-50%);
  }

  100% {
    width: 8px;
    height: 16px;
    border-radius: 8px 0 0 8px;
    border-left: 2px solid;
    border-right: none;
    opacity: 1;
    transform: translateY(-50%);
  }
}

/* 半圆的脉动效果 */
@keyframes semiCirclePulse {
  0% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
  50% {
    opacity: 1;
    transform: translateY(-50%) scale(1.1);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  }
  100% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
}

/* 左侧半圆脉动 */
@keyframes leftSemiCirclePulse {
  0% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
  50% {
    opacity: 1;
    transform: translateY(-50%) scale(1.1);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  }
  100% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
}

/* 右侧半圆脉动 */
@keyframes rightSemiCirclePulse {
  0% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
  50% {
    opacity: 1;
    transform: translateY(-50%) scale(1.1);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  }
  100% {
    opacity: 0.8;
    transform: translateY(-50%) scale(1);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
}

/* 应用动画类 */
.bubble-animation-morphToSemiCircle {
  animation: semiCirclePulse 2s ease-in-out infinite;
}

/* 左侧半圆的变换动画 */
.bubble-left.bubble-animation-morphToSemiCircle {
  animation:
    morphToLeftSemiCircle 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards,
    leftSemiCirclePulse 2s ease-in-out 0.8s infinite;
}

/* 右侧半圆的变换动画 */
.bubble-right.bubble-animation-morphToSemiCircle {
  animation:
    morphToRightSemiCircle 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards,
    rightSemiCirclePulse 2s ease-in-out 0.8s infinite;
}

/* TaskBar 重新出现动画已移除，保持简洁 */

/* === Tooltip 样式 === */
.task-tooltip {
  position: fixed;
  background: rgba(0, 0, 0, 0.9);
  color: white;
  padding: 12px;
  border-radius: 8px;
  font-size: 12px;
  z-index: 10000; /* 确保在最上层 */
  max-width: 250px;
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.4),
    0 4px 12px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  backdrop-filter: blur(4px); /* 增加模糊背景效果 */
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.tooltip-title {
  font-weight: 700;
  font-size: 13px;
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  color: #ffffff;
}

.tooltip-content {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tooltip-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 18px;
}

.tooltip-label {
  opacity: 0.8;
  min-width: 60px;
  color: #e5e5e5;
}

.tooltip-value {
  font-weight: 600;
  text-align: right;
  color: #ffffff;
}

.sticky-text {
  position: absolute;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  line-height: 1.2;
  z-index: 10;
}

/* 暗色主题支持 */
:global(html[data-theme='dark']) .task-bar {
  border-color: #111827 !important;
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.7),
    0 2px 4px rgba(0, 0, 0, 0.3) !important;
}

:global(html[data-theme='dark']) .task-bar:hover {
  box-shadow:
    0 6px 20px rgba(0, 0, 0, 0.8),
    0 4px 8px rgba(0, 0, 0, 0.4) !important;
  transform: translateY(-2px);
  transition: all 0.2s ease;
}

:global(html[data-theme='dark']) .task-bar:hover::after {
  background: rgba(7, 10, 15, 0.98) !important;
  color: #f9fafb !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6) !important;
}

:global(html[data-theme='dark']) .task-bar.normal {
  background: linear-gradient(135deg, #1e40af, #1e3a8a) !important;
  border-color: #1e3a8a !important;
}

:global(html[data-theme='dark']) .task-bar.milestone {
  background: linear-gradient(135deg, #c2410c, #9a3412) !important;
  border-color: #9a3412 !important;
}

:global(html[data-theme='dark']) .task-bar.completed {
  background: linear-gradient(135deg, #14532d, #16a34a) !important;
  border-color: #14532d !important;
}

:global(html[data-theme='dark']) .task-bar.delayed {
  background: linear-gradient(135deg, #991b1b, #dc2626) !important;
  border-color: #991b1b !important;
}

:global(html[data-theme='dark']) .task-bar.parent {
  background: linear-gradient(135deg, #581c87, #7c3aed) !important;
  border-color: #581c87 !important;
}

:global(html[data-theme='dark']) .task-content {
  color: #ffffff !important;
}

:global(html[data-theme='dark']) .task-name {
  color: #ffffff !important;
}

:global(html[data-theme='dark']) .progress-bar {
  background: rgba(255, 255, 255, 0.2) !important;
}

:global(html[data-theme='dark']) .progress-fill {
  background: rgba(255, 255, 255, 0.8) !important;
}

:global(html[data-theme='dark']) .resize-handle {
  background: rgba(255, 255, 255, 0.1) !important;
}

:global(html[data-theme='dark']) .resize-handle:hover {
  background: rgba(255, 255, 255, 0.3) !important;
}

/* 周视图下的短TaskBar样式优化 */
.task-bar.week-view.short-task-bar {
  position: relative;
  overflow: visible;
}

/* 周视图下短TaskBar的内容溢出效果 */
.task-bar.overflow-effect .task-bar-content {
  /* 保持与日视图一致的布局 */
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
  padding: 0 8px;
  font-size: 12px;
  font-weight: 500;
  text-align: center;
  overflow: visible;
  position: relative;
  z-index: 10;
  /* 确保主体内容区域仍可拖拽 */
  pointer-events: auto;
}

.task-bar.overflow-effect .task-name {
  /* 保持与日视图一致的样式 */
  white-space: nowrap;
  overflow: visible;
  line-height: 1.2;
  font-size: 12px;
  font-weight: 700;
  z-index: 15;
  pointer-events: none;
  /* 允许溢出显示，但保持原始样式 */
  min-width: max-content;
}

/* 周视图下进度百分比保持与日视图一致 */
.task-bar.overflow-effect .task-progress {
  /* 保持与日视图一致的基础样式 */
  opacity: 0.9;
  font-size: 11px;
  font-weight: 700;
  z-index: 16;
  pointer-events: none;
  padding: 1px 3px;
  border-radius: 2px;
}

/* 周视图下的TaskBar基础样式调整 */
.task-bar.week-view {
  min-width: 4px; /* 确保即使很短的任务也有最小可见宽度 */
  border-width: 1px;
  border-radius: 2px;
}

/* 暗色主题下的短TaskBar溢出效果 */
:global(html[data-theme='dark']) .task-bar.overflow-effect .resize-handle {
  background: rgba(255, 255, 255, 0.15);
}

:global(html[data-theme='dark']) .task-bar.overflow-effect .resize-handle:hover {
  background: rgba(255, 255, 255, 0.3);
}

/* 暗色主题下的进度百分比样式 */
:global(html[data-theme='dark']) .task-bar.overflow-effect .task-progress {
  background: rgba(0, 0, 0, 0.9);
  color: white;
}

:global(html[data-theme='dark']) .task-bar.week-view {
  border-color: var(--gantt-border-light, #555555);
}
</style>
