<template>
  <div class="gantt-schedule-page">
    <div class="gantt-header">
      <div class="gantt-header-left">
        <h1 class="page-title">甘特图排产</h1>
        <p class="page-desc">拖拽工序条块调整计划时间，完成后请保存排产草稿</p>
      </div>
      <n-space>
        <n-button type="primary" :loading="saving" @click="handleSaveDraft">保存排产草稿</n-button>
        <n-button @click="handleBack">返回</n-button>
      </n-space>
    </div>

    <div class="gantt-meta">
      <span>生产订单 {{ scheduleStats.moCount }} 条</span>
      <span class="meta-divider">|</span>
      <span>工序 {{ scheduleStats.routeCount }} 道</span>
      <span class="meta-divider">|</span>
      <span>今日 {{ todayLabel }}</span>
      <span class="meta-divider">|</span>
      <span class="meta-legend"><i class="legend-today-line" />今日时间轴</span>
    </div>

    <div class="gantt-panel">
      <div class="gantt-panel-head">
        <span class="panel-title">排产明细</span>
        <span class="panel-tip">主产品行展示物料编码与名称，子行展示工序号与工序名称</span>
      </div>
      <div class="gantt-container" ref="ganttContainer" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, toRaw, computed } from 'vue'
import { useMessage } from 'naive-ui'
import { useRouter } from 'vue-router'
import { gantt } from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
import { orderProjectApi, normalizeKingdeeProcessRoute, resolveProcessName, type KingdeePrdMo } from '@/api/orderProject'

const router = useRouter()
const ganttContainer = ref<HTMLElement | null>(null)
const message = useMessage()
let ganttEventIds: string[] = []

const tableData = ref<any[]>([])
const projectId = ref<number | null>(null)
const saving = ref(false)

const PROCESS_COLORS = [
  '#409eff', '#67c23a', '#e6a23c', '#909399', '#f56c6c',
  '#36cfc9', '#597ef7', '#9254de', '#73d13d',
]

const WEEKDAY_LABELS = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']

function getRouteColorIndex(index: number) {
  return index % PROCESS_COLORS.length
}

function getRouteColor(index: number) {
  return PROCESS_COLORS[getRouteColorIndex(index)]
}

function formatDayScale(date: Date) {
  return `${date.getDate()}日 ${WEEKDAY_LABELS[date.getDay()]}`
}

const scheduleStats = computed(() => {
  let routeCount = 0
  tableData.value.forEach((mo) => {
    routeCount += mo.processRoutes?.length ?? 0
  })
  return {
    moCount: tableData.value.length,
    routeCount,
  }
})

const todayLabel = computed(() => {
  const d = new Date()
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
})

function handleBack() {
  router.back()
}

async function handleSaveDraft() {
  if (!projectId.value) {
    message.warning('缺少项目ID，无法保存')
    return
  }

  saving.value = true
  try {
    const draftData = tableData.value.map(mo => {
      const { processRoutes, ...rest } = mo
      return { ...rest, children: processRoutes } as KingdeePrdMo
    })

    await orderProjectApi.saveKingdeeOrderDraft(projectId.value, draftData)
    message.success('排产草稿保存成功')
    sessionStorage.setItem('gantt_schedule_data', JSON.stringify(tableData.value))
  } finally {
    saving.value = false
  }
}

onMounted(async () => {
  try {
    initGantt()

    const stateStr = sessionStorage.getItem('gantt_schedule_data')
    const idStr = sessionStorage.getItem('gantt_project_id')

    if (idStr) {
      projectId.value = Number(idStr)
    }

    if (stateStr) {
      const parsedData = JSON.parse(stateStr)
      tableData.value = (Array.isArray(parsedData) ? parsedData : []).map((mo: any) => ({
        ...mo,
        processRoutes: (mo.processRoutes ?? []).map(normalizeKingdeeProcessRoute),
      }))
      renderGanttData()
    } else {
      message.warning('未获取到排产数据')
    }
  } catch {
    message.error('加载排产数据失败')
  }
})

function formatGanttDate(date: Date) {
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}

function formatGridDate(value: Date | string | undefined) {
  if (!value) return ''
  const date = value instanceof Date ? value : new Date(String(value).replace(' ', 'T'))
  if (isNaN(date.getTime())) return ''
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}

function formatMoMaterialLabel(mo: any) {
  const code = mo.materialCode?.trim() || ''
  const name = mo.materialName?.trim() || ''
  if (code && name) return `${code} ${name}`
  return code || name
}

function initGantt() {
  if (!ganttContainer.value) return

  ganttEventIds.forEach(id => gantt.detachEvent(id))
  ganttEventIds = []
  gantt.clearAll()

  gantt.config.date_format = '%Y-%m-%d %H:%i:%s'
  gantt.config.scale_height = 60
  gantt.config.row_height = 40
  gantt.config.bar_height = 26
  gantt.config.scales = [
    { unit: 'month', step: 1, format: '%Y年 %m月' },
    { unit: 'day', step: 1, format: formatDayScale },
  ]
  gantt.config.columns = [
    {
      name: 'text',
      label: '工序号 / 物料编码',
      tree: true,
      width: 200,
      resize: true,
      template(task: any) {
        if (task._type === 'route') return task.operNumber ?? ''
        return task.materialCode ?? ''
      },
    },
    {
      name: 'routeProcessName',
      label: '工序名称 / 物料名称',
      align: 'center',
      width: 168,
      resize: true,
      template(task: any) {
        if (task._type === 'route') {
          return task.routeProcessName ?? resolveProcessName(task._raw ?? {}) ?? ''
        }
        return task.materialName ?? ''
      },
    },
    {
      name: 'start_date',
      label: '计划开始',
      align: 'center',
      width: 118,
      template(task: any) {
        return formatGridDate(task.start_date)
      },
    },
    {
      name: 'end_date',
      label: '计划结束',
      align: 'center',
      width: 118,
      template(task: any) {
        return formatGridDate(task.end_date)
      },
    },
  ]

  gantt.config.layout = {
    css: 'gantt_container',
    cols: [
      {
        width: 620,
        minWidth: 420,
        maxWidth: 680,
        rows: [
          { view: 'grid', scrollX: 'gridScroll', scrollable: true, scrollY: 'scrollVer' },
          { view: 'scrollbar', id: 'gridScroll', group: 'horizontal' },
        ],
      },
      { resizer: true, width: 1 },
      {
        rows: [
          { view: 'timeline', scrollX: 'scrollHor', scrollY: 'scrollVer' },
          { view: 'scrollbar', id: 'scrollHor', group: 'horizontal' },
        ],
      },
      { view: 'scrollbar', id: 'scrollVer' },
    ],
  }

  gantt.config.drag_progress = false
  gantt.config.drag_links = false
  // 使用自然日历（含周六日），不按工作日历自动顺延
  gantt.config.work_time = false
  gantt.config.correct_work_time = false
  gantt.config.skip_off_time = false
  gantt.config.duration_unit = 'day'
  gantt.config.smart_rendering = true

  gantt.i18n.setLocale('cn')
  gantt.plugins({ marker: true })

  gantt.templates.task_text = function(_start, _end, task) {
    if (task._type === 'route') {
      return task.routeProcessName || resolveProcessName(task._raw ?? {}) || ''
    }
    return task.materialName || task.materialLabel || ''
  }

  gantt.templates.task_class = function(_start, _end, task) {
    if (task._type === 'mo') return 'gantt-task-mo'
    if (task._type === 'route') {
      return `gantt-task-route gantt-route-c-${task.colorIndex ?? 0}`
    }
    return ''
  }

  gantt.templates.grid_row_class = function(_start, _end, task) {
    if (task._type === 'mo') return 'gantt-row-mo'
    if (task._type === 'route') {
      return `gantt-row-route gantt-route-c-${task.colorIndex ?? 0}`
    }
    return ''
  }

  gantt.templates.timeline_cell_class = function(_task, date) {
    const day = date.getDay()
    if (day === 0 || day === 6) return 'gantt-weekend'
    return ''
  }

  gantt.init(ganttContainer.value)
}

/** 今日时间轴标记（数据刷新后需重新添加） */
function addTodayMarker() {
  const now = new Date()
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 12, 0, 0)
  gantt.addMarker({
    start_date: today,
    css: 'today-marker',
    text: '今日',
    title: `今日: ${formatGanttDate(now)}`,
  })
  gantt.showDate(today)
}

function getRouteProcessName(route: any) {
  return resolveProcessName(route)
}

function renderGanttData() {
  gantt.clearAll()
  const tasks: any[] = []
  const rawData = toRaw(tableData.value) || []

  rawData.forEach((mo: any, moIndex: number) => {
    const moId = `mo_${moIndex}_${mo.productionOrderNo}_${mo.billNo}`
    let minDate: Date | null = mo.planStartTime ? new Date(mo.planStartTime.replace(' ', 'T')) : null
    let maxDate: Date | null = mo.planFinishTime ? new Date(mo.planFinishTime.replace(' ', 'T')) : null
    if (!minDate || isNaN(minDate.getTime())) minDate = new Date()
    if (!maxDate || isNaN(maxDate.getTime())) maxDate = new Date(minDate.getTime() + 86400000)
    if (maxDate <= minDate) maxDate = new Date(minDate.getTime() + 86400000)

    const materialLabel = formatMoMaterialLabel(mo)
    tasks.push({
      id: moId,
      text: materialLabel,
      materialLabel,
      materialCode: mo.materialCode || '',
      materialName: mo.materialName || '',
      operNumber: '',
      processName: '',
      start_date: formatGanttDate(minDate),
      end_date: formatGanttDate(maxDate),
      open: true,
      type: gantt.config.types.project,
      _type: 'mo',
      progress: 0,
      _raw: mo,
    })

    if (mo.processRoutes?.length > 0) {
      mo.processRoutes.forEach((route: any, index: number) => {
        const routeId = `route_${moId}_${index}`
        let rStart = route.planStartTime ? new Date(route.planStartTime.replace(' ', 'T')) : null
        let rEnd = route.planFinishTime ? new Date(route.planFinishTime.replace(' ', 'T')) : null

        if (!rStart || isNaN(rStart.getTime())) rStart = minDate
        if (!rEnd || isNaN(rEnd.getTime())) rEnd = maxDate
        if (rEnd <= rStart) rEnd = new Date(rStart!.getTime() + 3600000)

        const operNumber = route.operNumber != null ? String(route.operNumber) : String(index + 1)
        const routeProcessName = getRouteProcessName(route)
        const colorIndex = getRouteColorIndex(index)
        const routeColor = getRouteColor(index)

        tasks.push({
          id: routeId,
          text: operNumber,
          operNumber,
          routeProcessName,
          colorIndex,
          routeColor,
          color: routeColor,
          start_date: formatGanttDate(rStart!),
          end_date: formatGanttDate(rEnd),
          parent: moId,
          _type: 'route',
          progress: 0,
          _raw: route,
        })
      })
    }
  })

  gantt.parse({ data: tasks, links: [] })
  addTodayMarker()

  const evDragId = gantt.attachEvent('onBeforeTaskDrag', (id) => {
    const task = gantt.getTask(id)
    return task.type !== gantt.config.types.project
  })
  ganttEventIds.push(evDragId)

  const evId = gantt.attachEvent('onAfterTaskDrag', (id, mode) => {
    const task = gantt.getTask(id)
    if (mode === 'move' || mode === 'resize') {
      const start = gantt.date.date_to_str(gantt.config.date_format)(task.start_date)
      const end = gantt.date.date_to_str(gantt.config.date_format)(task.end_date)

      if (task._type === 'mo') {
        task._raw.planStartTime = start
        task._raw.planFinishTime = end
      } else if (task._type === 'route') {
        task._raw.planStartTime = start
        task._raw.planFinishTime = end

        const parentId = task.parent
        if (parentId) {
          const parentTask = gantt.getTask(parentId)
          const children = gantt.getChildren(parentId)
          let minD: Date | null = null
          let maxD: Date | null = null

          children.forEach((childId: string | number) => {
            const childTask = gantt.getTask(childId)
            const cs = childTask.start_date
            const ce = childTask.end_date
            if (cs && (!minD || cs < minD)) minD = cs
            if (ce && (!maxD || ce > maxD)) maxD = ce
          })

          if (minD && maxD) {
            parentTask.start_date = new Date(minD)
            parentTask.end_date = new Date(maxD)
            parentTask._raw.planStartTime = gantt.date.date_to_str(gantt.config.date_format)(minD)
            parentTask._raw.planFinishTime = gantt.date.date_to_str(gantt.config.date_format)(maxD)
            gantt.updateTask(parentId)
          }
        }
      }

      const label = task._type === 'route'
        ? (task.routeProcessName || task.operNumber)
        : (task.materialLabel || task.materialName)
      message.success(`任务 [${label}] 计划时间已更新`)
    }
  })
  ganttEventIds.push(evId)
}

onUnmounted(() => {
  ganttEventIds.forEach(id => gantt.detachEvent(id))
  gantt.clearAll()
})
</script>

<style scoped>
.gantt-schedule-page {
  --gs-font: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', sans-serif;
  --gs-title: #333333;
  --gs-text: #606266;
  --gs-border: #e4e7ed;
  --gs-bg: #f5f7fa;

  display: flex;
  flex-direction: column;
  height: calc(100vh - 120px);
  padding: 16px;
  box-sizing: border-box;
  font-family: var(--gs-font);
  font-weight: 400;
  color: var(--gs-text);
  background: var(--gs-bg);
}

.gantt-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-shrink: 0;
  margin-bottom: 12px;
  padding: 14px 16px;
  background: #fff;
  border: 1px solid var(--gs-border);
  border-radius: 4px;
}

.gantt-header-left {
  min-width: 0;
}

.page-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--gs-title);
  line-height: 1.4;
}

.page-desc {
  margin: 4px 0 0;
  font-size: 13px;
  font-weight: 400;
  color: var(--gs-text);
  line-height: 1.5;
}

.gantt-meta {
  flex-shrink: 0;
  margin-bottom: 12px;
  padding: 0 4px;
  font-size: 13px;
  font-weight: 400;
  color: var(--gs-text);
}

.meta-divider {
  margin: 0 10px;
  color: #dcdfe6;
}

.meta-legend {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.legend-today-line {
  display: inline-block;
  width: 2px;
  height: 14px;
  background: #f56c6c;
  border-radius: 1px;
}

.gantt-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--gs-border);
  border-radius: 4px;
  overflow: hidden;
}

.gantt-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  background: #fafafa;
  border-bottom: 1px solid var(--gs-border);
  flex-shrink: 0;
}

.panel-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--gs-title);
}

.panel-tip {
  font-size: 12px;
  font-weight: 400;
  color: #909399;
}

.gantt-container {
  flex: 1;
  width: 100%;
  min-height: 0;
  overflow: hidden;
}

/* dhtmlx-gantt：仅表头标题加粗，其余正常字重 */
.gantt-container :deep(.gantt_container) {
  font-family: var(--gs-font);
  font-weight: 400;
  border: none !important;
  background: #fff;
}

.gantt-container :deep(.gantt_grid_scale),
.gantt-container :deep(.gantt_task_scale) {
  background: #fafafa;
  border-bottom: 1px solid var(--gs-border) !important;
}

.gantt-container :deep(.gantt_task_scale .gantt_scale_cell) {
  font-size: 12px;
  font-weight: 400;
  color: var(--gs-text);
  border-color: var(--gs-border) !important;
  line-height: 1.3;
  white-space: normal;
}

.gantt-container :deep(.gantt_grid_scale .gantt_grid_head_cell) {
  font-size: 13px;
  font-weight: 600;
  color: var(--gs-title);
  border-color: var(--gs-border) !important;
}

/* 工序号文字与进度条同色（无圆点） */
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-0 .gantt_cell:first-child) { color: #409eff; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-1 .gantt_cell:first-child) { color: #67c23a; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-2 .gantt_cell:first-child) { color: #e6a23c; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-3 .gantt_cell:first-child) { color: #909399; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-4 .gantt_cell:first-child) { color: #f56c6c; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-5 .gantt_cell:first-child) { color: #36cfc9; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-6 .gantt_cell:first-child) { color: #597ef7; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-7 .gantt_cell:first-child) { color: #9254de; }
.gantt-container :deep(.gantt_row.gantt-row-route.gantt-route-c-8 .gantt_cell:first-child) { color: #73d13d; }

.gantt-container :deep(.gantt_task_line.gantt-route-c-0) { background-color: #409eff !important; border-color: #409eff !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-1) { background-color: #67c23a !important; border-color: #67c23a !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-2) { background-color: #e6a23c !important; border-color: #e6a23c !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-3) { background-color: #909399 !important; border-color: #909399 !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-4) { background-color: #f56c6c !important; border-color: #f56c6c !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-5) { background-color: #36cfc9 !important; border-color: #36cfc9 !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-6) { background-color: #597ef7 !important; border-color: #597ef7 !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-7) { background-color: #9254de !important; border-color: #9254de !important; }
.gantt-container :deep(.gantt_task_line.gantt-route-c-8) { background-color: #73d13d !important; border-color: #73d13d !important; }

.gantt-container :deep(.gantt_task_line.gantt-task-route .gantt_task_progress) {
  background-color: rgba(0, 0, 0, 0.18) !important;
  opacity: 1;
}

.gantt-container :deep(.gantt_task_line.gantt-task-route) {
  border: none !important;
  border-radius: 3px;
}

.gantt-container :deep(.gantt_grid_data .gantt_cell) {
  font-size: 13px;
  font-weight: 400;
  color: var(--gs-text);
  border-color: #f0f2f5 !important;
}

.gantt-container :deep(.gantt_row.gantt-row-mo) {
  background: #fafafa;
}

.gantt-container :deep(.gantt_row.gantt-row-mo .gantt_cell) {
  font-weight: 400;
  color: var(--gs-title);
}

.gantt-container :deep(.gantt_row:hover) {
  background: #f5f7fa !important;
}

.gantt-container :deep(.gantt_task_row.gantt_selected),
.gantt-container :deep(.gantt_row.gantt_selected) {
  background: #ecf5ff !important;
}

.gantt-container :deep(.gantt_task_line.gantt-task-route .gantt_task_content) {
  font-size: 12px;
  font-weight: 400;
  color: #fff;
  padding: 0 6px;
}

.gantt-container :deep(.gantt_task_line.gantt_project) {
  display: none;
}

.gantt-container :deep(.gantt_weekend) {
  background: #fafafa;
}

.gantt-container :deep(.gantt_marker.today-marker) {
  background: #f56c6c;
  width: 2px;
  z-index: 4;
  opacity: 1;
}

.gantt-container :deep(.today-marker .gantt_marker_content) {
  background: #f56c6c;
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  border-radius: 2px;
  padding: 2px 8px;
  box-shadow: 0 1px 4px rgba(245, 108, 108, 0.35);
}

.gantt-container :deep(.gantt_layout_content) {
  border-color: var(--gs-border) !important;
}

.gantt-container :deep(.gantt_hor_scroll),
.gantt-container :deep(.gantt_ver_scroll) {
  background: #fafafa;
}
</style>
