<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef, GridApi, GridReadyEvent } from 'ag-grid-community'
import { taskList } from '@/sql'
import { myTheme, gridOptions, agGridWrapClass, agGridStyle } from '@/lib/diyAgGridOptions'
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
import { parseNumberOrNull } from '@/lib/number'
import { withReadonlyAutoHeight } from '@/lib/agGridReadonlyAutoHeight'
import { syncPricingTotalToZxFw } from '@/lib/zxFwPricingSync'
import { useZxFwPricingStore } from '@/pinia/zxFwPricing'
import { useKvStore } from '@/pinia/kv'
import { loadConsultCategoryFactorMap } from '@/lib/xmFactorDefaults'
import { usePricingPaneLifecycle } from '@/lib/pricingScalePaneLifecycle'
import { sumNullableBy } from '@/lib/pricingScaleCalc'
import { createPinnedTopRowData } from '@/lib/pricingPinnedRows'
import MethodUnavailableNotice from '@/features/shared/components/MethodUnavailableNotice.vue'
import { buildProjectScopedSessionKey } from '@/lib/pricingPersistControl'

import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale';
import {useDataStore} from "@/pinia/zx";

interface DetailRow {
  id: string
  taskCode: string
  taskName: string
  unit: string
  conversion: number | null
  workload: number | null
  basicFee: number | null
  budgetBase: string
  budgetReferenceUnitPrice: string | number | null
  budgetAdoptedUnitPrice: number | null
  consultCategoryFactor: number | null
  cLow: number | null
  cMid: number | null
  cHigh: number | null
  serviceFee: number | null
  remark: string
  path: string[]
}

interface XmInfoState {
  projectName: string
  detailRows: DetailRow[]
}

const props = defineProps<{
  contractId: string,
  contractName: string,
  serviceId: string | number
}>()
const zxFwPricingStore = useZxFwPricingStore()
const { t, locale } = useI18n()
const kvStore = useKvStore()
const DB_KEY = computed(() => `gzlF-${props.contractId}-${props.serviceId}`)
const HT_CONSULT_FACTOR_KEY = computed(() => `ht-consult-category-factor-v1-${props.contractId}`)
const PRICING_CLEAR_SKIP_PREFIX = 'pricing-clear-skip:'
const PRICING_FORCE_DEFAULT_PREFIX = 'pricing-force-default:'
const consultCategoryFactorMap = ref<Map<string, number | null>>(new Map())
let factorDefaultsLoaded = false
const paneInstanceCreatedAt = Date.now()
const gridApi = ref<GridApi<DetailRow> | null>(null)

/**
 * 获取服务对应的咨询服务分类因子
 * @returns 咨询服务分类因子值，如果没有则返回 null
 */
const getDefaultConsultCategoryFactor = () =>
  consultCategoryFactorMap.value.get(String(props.serviceId)) ?? null

/**
 * 确保咨询服务分类因子默认值已加载
 * 从合同段的咨询因子配置中加载，避免重复加载
 */
const ensureFactorDefaultsLoaded = async () => {
  if (factorDefaultsLoaded) return
  consultCategoryFactorMap.value = await loadConsultCategoryFactorMap(HT_CONSULT_FACTOR_KEY.value)
  factorDefaultsLoaded = true
}

/**
 * 检查是否应该跳过数据持久化
 * 用于在清空操作后的短时间内避免旧数据回填
 * @returns 是否应该跳过持久化
 */
const shouldSkipPersist = () => {
  const storageKey = buildProjectScopedSessionKey(PRICING_CLEAR_SKIP_PREFIX, DB_KEY.value)
  const raw = sessionStorage.getItem(storageKey)
  if (!raw) return false
  const now = Date.now()

  if (raw.includes(':')) {
    const [issuedRaw, untilRaw] = raw.split(':')
    const issuedAt = Number(issuedRaw)
    const skipUntil = Number(untilRaw)
    if (Number.isFinite(issuedAt) && Number.isFinite(skipUntil) && now <= skipUntil) {
      return paneInstanceCreatedAt <= issuedAt
    }
    sessionStorage.removeItem(storageKey)
    return false
  }

  const skipUntil = Number(raw)
  if (Number.isFinite(skipUntil) && now <= skipUntil) return true
  sessionStorage.removeItem(storageKey)
  return false
}

/**
 * 检查是否应该强制加载默认数据
 * 用于在恢复默认操作后强制使用初始值
 * @returns 是否应该强制加载默认数据
 */
const shouldForceDefaultLoad = () => {
  const storageKey = buildProjectScopedSessionKey(PRICING_FORCE_DEFAULT_PREFIX, DB_KEY.value)
  const raw = sessionStorage.getItem(storageKey)
  if (!raw) return false
  const forceUntil = Number(raw)
  sessionStorage.removeItem(storageKey)
  return Number.isFinite(forceUntil) && Date.now() <= forceUntil
}

/**
 * 获取当前服务工作量法的状态
 * @returns 工作量法状态对象，包含明细行数据
 */
const getMethodState = () =>
  zxFwPricingStore.getServicePricingMethodState<DetailRow>(props.contractId, props.serviceId, 'serviceFee')

const detailRows = computed<DetailRow[]>({
  get: () => {
    const rows = getMethodState()?.detailRows
    return Array.isArray(rows) ? rows : []
  },
  set: rows => {
    zxFwPricingStore.setServicePricingMethodState(props.contractId, props.serviceId, 'serviceFee', {
      detailRows: rows
    })
  }
})

type taskLite = {
  serviceID: number
  code?: string
  ref?: string
  name: string
  basicParam: string
  unit: string
  conversion: number | null
  maxPrice: number | null
  minPrice: number | null
  defPrice: number | null
  midPrice: number | null
  highPrice: number | null
  desc: string | null
}

/**
 * 获取任务显示名称
 * 根据当前语言环境返回中文或英文名称
 * @param task 任务对象
 * @returns 任务显示名称
 */
const getTaskDisplayName = (task: taskLite | undefined) => {
  if (!task) return ''
  return String(locale.value).toLowerCase().startsWith('en')
    ? (task as taskLite & { nameEn?: string }).nameEn || task.name
    : task.name
}

/**
 * 格式化任务参考单价
 * 根据任务的最小/最大价格生成价格区间字符串
 * @param task 任务对象
 * @returns 格式化后的价格区间字符串（如 "100元-200元"）
 */
const formatTaskReferenceUnitPrice = (task: taskLite) => {
  const unit = task.unit || ''
  const hasMin = typeof task.minPrice === 'number' && Number.isFinite(task.minPrice)
  const hasMax = typeof task.maxPrice === 'number' && Number.isFinite(task.maxPrice)
  if (hasMin && hasMax) return `${task.minPrice}${unit}-${task.maxPrice}${unit}`
  if (hasMin) return `${task.minPrice}${unit}`
  if (hasMax) return `${task.maxPrice}${unit}`
  return ''
}

/**
 * 获取当前服务对应的工作任务 ID 列表
 * 从 taskList 中筛选出属于当前服务的任务，并按 ID 排序
 * @returns 任务 ID 数组
 */
const getSourceTaskIds = () => {
  // 从 serviceId 中提取最后的数字部分，例如：ct-1776069559338-f621-zx-9 -> 9
  const serviceIdStr = String(props.serviceId)
  const match = serviceIdStr.match(/-(\d+)$/)
  const currentServiceId = match ? Number(match[1]) : NaN
  return Object.entries(taskList as Record<string, taskLite>)
    .filter(([, task]) => Number(task.serviceID) === currentServiceId)
    .map(([key]) => Number(key))
    .filter(Number.isFinite)
    .sort((a, b) => a - b)
}

const isWorkloadMethodApplicable = computed(() => getSourceTaskIds().length > 0)

/**
 * 构建默认明细行数据
 * 根据当前服务的任务列表生成初始行数据
 * @returns 默认明细行数组
 */
const buildDefaultRows = (): DetailRow[] => {
  const rows: DetailRow[] = []
  const sourceTaskIds = getSourceTaskIds()

  for (const [order, taskId] of sourceTaskIds.entries()) {
    const task = (taskList as Record<string, taskLite | undefined>)[String(taskId)]
    const taskCode = task?.code || task?.ref || ''
    if (!taskCode || !task?.name) continue
    const contractId = props.contractId;
    const rowId = `${contractId}-task-${taskId}`
    rows.push({
      id: rowId,
      taskCode,
      taskName: getTaskDisplayName(task),
      unit: task.unit || '',
      conversion: typeof task.conversion === 'number' && Number.isFinite(task.conversion) ? task.conversion : null,
      workload: typeof task.midPrice === 'number' && Number.isFinite(task.midPrice) ? task.midPrice : null,
      basicFee: null,
      budgetBase: task.basicParam || '',
      budgetReferenceUnitPrice: null,
      budgetAdoptedUnitPrice:
        typeof task.defPrice === 'number' && Number.isFinite(task.defPrice) ? task.defPrice : null,
      consultCategoryFactor: typeof task.highPrice === 'number' && Number.isFinite(task.highPrice) ? task.highPrice : null,
      serviceFee: null,
      remark: task.desc|| '',
      type: 'task',
      path: [rowId]
    })
  }

  return rows
}

const isNoTaskRow = (row: DetailRow | undefined) => row?.id?.startsWith('task-none-') ?? false

/**
 * 合并数据库中的行数据与默认行数据
 * 保留用户编辑的值（工作量、单价、因子等），缺失时使用默认值
 * @param rowsFromDb 从数据库加载的行数据
 * @returns 合并后的明细行数组
 */
const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => {
  const dbValueMap = new Map<string, DetailRow>()
  for (const row of rowsFromDb || []) {
    dbValueMap.set(row.id, row)
  }

  return buildDefaultRows().map(row => {
    const fromDb = dbValueMap.get(row.id)
    if (!fromDb) return row
    const hasRemark = Object.prototype.hasOwnProperty.call(fromDb, 'remark')
    const hasConsultCategoryFactor = Object.prototype.hasOwnProperty.call(fromDb, 'consultCategoryFactor')
    // ✅ 计算 cLow 值
    let cLowValue: number | null = null
    let cMidValue: number | null = null
    let cHighValue: number | null = null
    const refPrice = fromDb.budgetReferenceUnitPrice
    const lowPrice = row.budgetAdoptedUnitPrice
    const midPrice = row.workload
    const highPrice = row.consultCategoryFactor
    cLowValue = refPrice * lowPrice
    cMidValue = refPrice * midPrice
    cHighValue = refPrice * highPrice
    let serviceFee = cMidValue

    if (fromDb.serviceFee != null && fromDb.serviceFee != 0) {
      serviceFee = fromDb.serviceFee
    }
    return {
      ...row,
      workload: typeof fromDb.workload === 'number' ? fromDb.workload : null,
      basicFee: typeof fromDb.basicFee === 'number' ? fromDb.basicFee : null,
      budgetReferenceUnitPrice: fromDb.budgetReferenceUnitPrice ?? row.budgetReferenceUnitPrice,
      budgetAdoptedUnitPrice: 1, // 固定为 1
      cLow: cLowValue,
      cMid: cMidValue,
      cHigh: cHighValue,
      consultCategoryFactor:
        typeof fromDb.consultCategoryFactor === 'number'
          ? fromDb.consultCategoryFactor
          : hasConsultCategoryFactor
            ? null
            : getDefaultConsultCategoryFactor(),
      serviceFee: serviceFee,
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : hasRemark ? '' : row.remark
    }
  })
}

const parseSanitizedNumberOrNull = (value: unknown) =>
  parseNumberOrNull(value, { sanitize: true, precision: 3 })

const parseSanitizedAdoptedPriceOrNull = (value: unknown) =>
  parseNumberOrNull(value, { sanitize: true, precision: 6 })

/**
 * 计算基础费用
 * 公式：基础费用 = 采用单价 × 换算系数 × 工作量
 * @param row 明细行数据
 * @returns 基础费用（保留2位小数），如果数据不完整则返回 null
 */
const calcBasicFee = (row: DetailRow | undefined) => {
  if (!row || isNoTaskRow(row)) return null
  const price = row.budgetAdoptedUnitPrice
  const conversion = row.conversion
  const workload = row.workload
  if (
    typeof price !== 'number' ||
    !Number.isFinite(price) ||
    typeof conversion !== 'number' ||
    !Number.isFinite(conversion) ||
    typeof workload !== 'number' ||
    !Number.isFinite(workload)
  ) {
    return null
  }
  return roundTo(toDecimal(price).mul(conversion).mul(workload), 2)
}

/**
 * 计算服务费用
 * 公式：服务费用 = 基础费用 × 咨询服务分类因子
 * @param row 明细行数据
 * @returns 服务费用（保留2位小数），如果数据不完整则返回 null
 */
const calcServiceFee = (row: DetailRow | undefined) => {
  if (!row || isNoTaskRow(row)) return null
  const factor = row.consultCategoryFactor
  const basicFee = calcBasicFee(row)
  if (
    basicFee == null ||
    typeof factor !== 'number' ||
    !Number.isFinite(factor)
  ) {
    return null
  }
  return roundTo(toDecimal(basicFee).mul(factor), 2)
}

/**
 * 格式化可编辑数字单元格
 * 根据行类型和值状态返回相应的显示文本
 * @param params AG Grid 单元格参数
 * @returns 格式化后的显示文本
 */
const formatEditableNumber = (params: any) => {
  if (isNoTaskRow(params.data)) return t('workloadPricing.none')
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return t('workloadPricing.clickToInput')
  }
  if (params.value == null) return ''
  return formatThousandsFlexible(params.value, 3)
}

/**
 * 判断两行是否应该合并显示（按任务名称和预算基数）
 * 用于 AG Grid 的 spanRows 功能，相同任务和预算基数的行合并显示
 * @param params AG Grid 行比较参数
 * @returns 是否应该合并
 */
const spanRowsByTaskName = (params: any) => {
  const rowA = params?.nodeA?.data as DetailRow | undefined
  const rowB = params?.nodeB?.data as DetailRow | undefined
  if (!rowA || !rowB) return false
  if (isNoTaskRow(rowA) || isNoTaskRow(rowB)) return false

  return Boolean(rowA.taskName) && Boolean(rowA.budgetBase) && rowA.taskName === rowB.taskName && rowA.budgetBase === rowB.budgetBase
}

const columnDefs: ColDef<DetailRow>[] = [
  {
    headerName: t('workloadPricing.columns.code'),
    field: 'taskCode',
    minWidth: 100,
    width: 120,
    pinned: 'left',
    colSpan: params => (params.node?.rowPinned ? 2 : 1),
    valueFormatter: params => (params.node?.rowPinned ? t('workloadPricing.total') : params.value || '')
  },
  {
    headerName: t('workloadPricing.columns.name'),
    field: 'taskName',
    minWidth: 150,
    width: 220,
    pinned: 'left',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },

    spanRows: true,
    valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
  },
  {
    headerName: t('workloadPricing.columns.budgetBase'),
    field: 'budgetBase',
    minWidth: 150,
    autoHeight: true,
    width: 180,
    colSpan: params => (params.node?.rowPinned ? 3 : 1),
    spanRows: spanRowsByTaskName,
    valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
  },
  {
    headerName: t('workloadPricing.columns.budgetReferenceUnitPrice'),
    field: 'budgetReferenceUnitPrice',
    minWidth: 170,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    valueFormatter: formatEditableNumber
  },
  /*{
    headerName: t('workloadPricing.columns.budgetAdoptedUnitPrice'),
    field: 'budgetAdoptedUnitPrice',
    headerClass: 'ag-right-aligned-header',
    minWidth: 170,
    flex: 1,
    // editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'ag-right-aligned-cell': () => true,
      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    valueParser: params => parseSanitizedAdoptedPriceOrNull(params.newValue),
    valueFormatter: params => {
      if (isNoTaskRow(params.data)) return t('workloadPricing.none')
      if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
        return t('workloadPricing.clickToInput')
      }
      if (params.value == null) return ''
      const unit = params.data?.unit || ''
      return `${formatThousandsFlexible(params.value, 6)}${unit}`
    }
  },*/
  {
    headerName: t('workloadPricing.columns.budgetAdoptedUnitPrice'),
    field: 'budgetAdoptedUnitPrice',
    headerClass: 'ag-right-aligned-header',
    minWidth: 170,
    flex: 1,
    // ❌ 移除 editable 或设置为 false
    editable: false,
    // ✅ 添加单元格样式
    cellClass: 'ag-right-aligned-cell',
    // ✅ 移除 cellClassRules 中的 editable-cell 相关规则
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    // ✅ 固定显示值为 1
    valueFormatter: params => {
      const unit = params.data?.unit || ''
      return `1`
      // return `1${unit}`
    },
    // ✅ 阻止任何编辑尝试
    valueParser: () => 1,
    // ✅ 禁用单元格编辑
    cellEditor: undefined
  },
  {
    headerName: t('workloadPricing.columns.workload'),
    field: 'workload',
    headerClass: 'ag-right-aligned-header',
    minWidth: 170,
    flex: 1,
    // ❌ 移除 editable 或设置为 false
    editable: false,
    // ✅ 添加单元格样式
    cellClass: 'ag-right-aligned-cell',
    // ✅ 移除 cellClassRules 中的 editable-cell 相关规则
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    // ✅ 固定显示值为 1
    valueFormatter: params => {
      const unit = params.data?.unit || ''
      return `2`
      // return `1${unit}`
    },
    // ✅ 阻止任何编辑尝试
    valueParser: () => 1,
    // ✅ 禁用单元格编辑
    cellEditor: undefined
  },
  /*{
    headerName: t('workloadPricing.columns.workload'),
    field: 'workload',
    minWidth: 140,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
                'ag-right-aligned-cell': () => true,

      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    aggFunc: decimalAggSum,
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber
  },*/
  {
    headerName: t('workloadPricing.columns.consultCategoryFactor'),
    field: 'consultCategoryFactor',
    headerClass: 'ag-right-aligned-header',
    minWidth: 170,
    flex: 1,
    // ❌ 移除 editable 或设置为 false
    editable: false,
    // ✅ 添加单元格样式
    cellClass: 'ag-right-aligned-cell',
    // ✅ 移除 cellClassRules 中的 editable-cell 相关规则
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    // ✅ 固定显示值为 1
    valueFormatter: params => {
      const unit = params.data?.unit || ''
      return `3`
      // return `1${unit}`
    },
    // ✅ 阻止任何编辑尝试
    valueParser: () => 1,
    // ✅ 禁用单元格编辑
    cellEditor: undefined
  },
  /*{
    headerName: t('workloadPricing.columns.consultCategoryFactor'),
    field: 'consultCategoryFactor',
    width: 80,
    minWidth: 70,
    maxWidth: 90,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
                'ag-right-aligned-cell': () => true,

      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber
  },*/
  {
    headerName: t('workloadPricing.columns.cLow'),
    field: 'cLow',
    width: 100,
    minWidth: 70,
    maxWidth: 120,
    // ✅ 设置为不可编辑
    editable: false,
    // ✅ 只读样式
    cellClass: 'ag-right-aligned-cell',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    }/*,
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber*/
  },
  {
    headerName: t('workloadPricing.columns.cMid'),
    field: 'cMid',
    width: 100,
    minWidth: 70,
    maxWidth: 120,
    // ✅ 设置为不可编辑
    editable: false,
    // ✅ 只读样式
    cellClass: 'ag-right-aligned-cell',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    }/*,
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber*/
  },
  {
    headerName: t('workloadPricing.columns.cHigh'),
    field: 'cHigh',
    width: 100,
    minWidth: 70,
    maxWidth: 120,
    // ✅ 设置为不可编辑
    editable: false,
    // ✅ 只读样式
    cellClass: 'ag-right-aligned-cell',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    }/*,
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber*/
  },
  {
    headerName: t('workloadPricing.columns.serviceFee'),
    field: 'serviceFee',
    headerClass: 'ag-right-aligned-header',
    minWidth: 150,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'ag-right-aligned-cell': () => true,

      'editable-cell-empty': params =>
          !params.node?.group &&
          !params.node?.rowPinned &&
          !isNoTaskRow(params.data) &&
          (params.value == null || params.value === '')
    },
    valueParser: params => parseSanitizedNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber
  },
  {
    headerName: t('workloadPricing.columns.remark'),
    field: 'remark',
    minWidth: 180,
    flex: 1.2,
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },

    editable: false,
    valueFormatter: params => {
    
      return params.value || ''
    },
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? ' remark-wrap-cell' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    }
  }
]
const gridColumnDefs = computed(() => withReadonlyAutoHeight(columnDefs))

const totalWorkload = computed(() => {
  return 1;
})
const totalBasicFee = computed(() => sumByNumber(detailRows.value, row => calcBasicFee(row)))
const totalServiceFee = computed(() => sumNullableBy(detailRows.value, row => calcServiceFee(row)))
const pinnedTopRowData = computed(() =>
  createPinnedTopRowData({
    id: 'pinned-total-row',
    taskCode: t('workloadPricing.total'),
    taskName: '',
    unit: '',
    conversion: null,
    workload: totalWorkload.value,
    basicFee: totalBasicFee.value,
    budgetBase: '',
    budgetReferenceUnitPrice: '',
    budgetAdoptedUnitPrice: null,
    consultCategoryFactor: null,
    serviceFee: totalServiceFee.value,
    remark: '',
    path: ['TOTAL']
  })
)



/**
 * 构建用于持久化的明细行数据
 * 计算并附加基础费用和服务费用字段
 * @returns 包含计算字段的明细行数组
 */
const buildPersistDetailRows = () =>
    detailRows.value.map(row => ({
      ...row,
      basicFee: calcBasicFee(row),
      serviceFee: row.serviceFee
    }))

/**
 * 保存数据到 IndexedDB 并同步到 zxFw store
 * 保存时机：单元格编辑、批量操作结束、组件失活/卸载时
 * 同步工作量法的总服务费用到咨询服务汇总表
 */
const saveToIndexedDB = async () => {
  try {
    const rows = detailRows.value.map(row => ({ ...row, type:  `${props.contractId}-task`}))
    const stats = await useDataStore().upsertBatch(rows)
    console.log('💾 数据保存成功:', stats)
    /*zxFwPricingStore.setServicePricingMethodState(
      props.contractId,
      props.serviceId,
      'serviceFee',
      payload,
      { force: true }
    )*/
    // const synced = await syncPricingTotalToZxFw({
    //   contractId: props.contractId,
    //   serviceId: props.serviceId,
    //   field: 'serviceFee',
    //   value: totalWorkload.value
    // })
    // if (!synced) return
  } catch (error) {
    console.error('saveToIndexedDB failed:', error)
  }
}

/**
 * 判断两个可空数值是否相等（考虑精度）
 * @param left 第一个数值
 * @param right 第二个数值
 * @returns 是否相等
 */
const isSameNullableNumber = (left: number | null | undefined, right: number | null | undefined) => {
  if (left == null && right == null) return true
  if (left == null || right == null) return false
  return roundTo(left, 6) === roundTo(right, 6)
}

/**
 * 从合同段同步咨询服务分类因子
 * 当合同段的咨询因子配置变化时，更新当前工作量的因子默认值
 */
const syncLinkedConsultFactorFromHt = async () => {
  if (!isWorkloadMethodApplicable.value || detailRows.value.length === 0) return
  consultCategoryFactorMap.value = await loadConsultCategoryFactorMap(HT_CONSULT_FACTOR_KEY.value)
  factorDefaultsLoaded = true
  const nextDefaultFactor = getDefaultConsultCategoryFactor()
  let changed = false
  detailRows.value = detailRows.value.map(row => {
    if (isSameNullableNumber(row.consultCategoryFactor, nextDefaultFactor)) return row
    changed = true
    return {
      ...row,
      consultCategoryFactor: nextDefaultFactor
    }
  })
  if (!changed) return
  await saveToIndexedDB()
}

const linkedConsultFactorSignature = computed(() => JSON.stringify({
  consultFactor:
    zxFwPricingStore.keyedStates[HT_CONSULT_FACTOR_KEY.value]
    ?? kvStore.entries[HT_CONSULT_FACTOR_KEY.value]
    ?? null
}))

/**
 * 从 IndexedDB 加载工作量法数据
 * 加载时机：组件挂载、激活、storageKey 变化时
 * 优先加载历史数据，没有则使用默认数据
 */
const loadFromIndexedDB = async () => {
  try {
    if (!isWorkloadMethodApplicable.value) {
      detailRows.value = []
      return
    }
    const taskRows = await useDataStore().query([
      { field: 'type', value: `${props.contractId}-task`, operator: 'eq' }
    ])
    /*await ensureFactorDefaultsLoaded()

    if (shouldForceDefaultLoad()) {
      detailRows.value = buildDefaultRows()
      return
    }*/

    // const data = await zxFwPricingStore.loadServicePricingMethodState<DetailRow>(props.contractId, props.serviceId, 'serviceFee')
    if (taskRows) {
      detailRows.value = mergeWithDictRows(taskRows)
      return
    }

    detailRows.value = buildDefaultRows()
  } catch (error) {
    console.error('loadFromIndexedDB failed:', error)
    detailRows.value = buildDefaultRows()
  }
}

/**
 * 根据任务词典更新行标签
 * 当语言切换或任务词典更新时，同步更新任务名称、单位等信息
 */
const relabelRowsFromTaskDict = async () => {
  if (!isWorkloadMethodApplicable.value || detailRows.value.length === 0) return
  let changed = false
  detailRows.value = detailRows.value.map(row => {
    if (isNoTaskRow(row)) return row
    const match = String(row.id || '').match(/^task-(\d+)-\d+$/)
    if (!match) return row
    const task = (taskList as Record<string, taskLite | undefined>)[match[1]]
    if (!task) return row
    const nextTaskName = getTaskDisplayName(task)
    const nextUnit = task.unit || ''
    const nextBudgetBase = task.basicParam || ''
    const nextBudgetReferenceUnitPrice = formatTaskReferenceUnitPrice(task)
    if (
      row.taskName === nextTaskName &&
      row.unit === nextUnit &&
      row.budgetBase === nextBudgetBase &&
      row.budgetReferenceUnitPrice === nextBudgetReferenceUnitPrice
    ) {
      return row
    }
    changed = true
    return {
      ...row,
      taskName: nextTaskName,
      unit: nextUnit,
      budgetBase: nextBudgetBase,
      budgetReferenceUnitPrice: nextBudgetReferenceUnitPrice
    }
  })
  gridApi.value?.refreshCells({ force: true })
  if (!changed) return
  await saveToIndexedDB()
}

let isBulkClipboardMutation = false

/**
 * 提交网格变更并保存
 * 在单元格编辑完成后调用，触发数据持久化
 */
const commitGridChanges = () => {
  void saveToIndexedDB()
}

/**
 * 处理单元格值变化事件
 * 触发时机：用户编辑完单元格后
 * 批量操作期间跳过，避免重复保存
 */
const handleCellValueChanged = () => {
  if (isBulkClipboardMutation) return
  commitGridChanges()
}

/**
 * 处理批量粘贴/填充操作开始
 * 设置标志位，避免在批量操作过程中频繁保存
 */
const handleBulkMutationStart = () => {
  isBulkClipboardMutation = true
}

/**
 * 处理批量粘贴/填充操作结束
 * 清除标志位并保存所有变更
 */
const handleBulkMutationEnd = () => {
  isBulkClipboardMutation = false
  commitGridChanges()
}

/**
 * AG Grid 初始化完成回调
 * 记录 gridApi 实例，用于后续刷新单元格等操作
 * @param event AG Grid 初始化事件
 */
const handleGridReady = (event: GridReadyEvent<DetailRow>) => {
  gridApi.value = event.api
}

usePricingPaneLifecycle({
  gridApi,
  loadFromIndexedDB,
  syncLinkedFields: syncLinkedConsultFactorFromHt,
  linkedSourceSignature: linkedConsultFactorSignature,
  saveToIndexedDB
})
watch(
  () => locale.value,
  () => {
    void relabelRowsFromTaskDict()
  }
)
watch(
    () => useDataStore().items,
    async (newItems) => {
      if (Object.keys(newItems).length > 0) {
        loadFromIndexedDB();
      }
    },
    { immediate: true, deep: true }
)
/**
 * 处理复制到剪贴板的单元格数据
 * 将数组类型的数据转换为 JSON 字符串
 * @param params AG Grid 剪贴板参数
 * @returns 处理后的单元格值
 */
const processCellForClipboard = (params: any) => {
  if (Array.isArray(params.value)) {
    return JSON.stringify(params.value); // 数组转字符串复制
  }
  return params.value;
};

/**
 * 处理从剪贴板粘贴的单元格数据
 * 根据字段类型解析数值（单价、工作量、因子等）
 * @param params AG Grid 剪贴板参数
 * @returns 解析后的单元格值
 */
const processCellFromClipboard = (params: any) => {
  const field = String(params.column?.getColDef?.().field || '')
  if (field === 'budgetAdoptedUnitPrice') {
    return parseSanitizedAdoptedPriceOrNull(params.value)
  }
  if (field === 'workload' || field === 'consultCategoryFactor') {
    return parseSanitizedNumberOrNull(params.value)
  }
  try {
    const parsed = JSON.parse(params.value);
    if (Array.isArray(parsed)) return parsed;
  } catch (e) {
    // 解析失败时返回原始值，无需额外处理
  }
  return params.value;
};

const mydiyTheme = myTheme.withParams({
  rowBorder: {
    style: "solid",
    width: 0.8,
    color: "var(--border)"
  },
  columnBorder: {
    style: "solid",
    width: 0.8,
    color: "var(--border)"
  }
})
</script>

<template>
  <div class="h-full min-h-0 flex flex-col">


    <div class="rounded-lg border bg-card xmMx flex min-h-0 flex-1 flex-col">
      <div class="flex items-center justify-between border-b px-4 py-3">
        <h3 class="text-sm font-semibold text-foreground">{{ t('workloadPricing.title') }}</h3>
        <div class="text-xs text-muted-foreground"></div>
      </div>

      <div v-if="isWorkloadMethodApplicable" :class="agGridWrapClass">
        <AgGridVue :style="agGridStyle" :rowData="detailRows"
          :columnDefs="gridColumnDefs" :gridOptions="gridOptions" :theme="mydiyTheme" :treeData="false"
          :animateRows="true"
          :enableCellSpan="true"
          @grid-ready="handleGridReady"
          @cell-value-changed="handleCellValueChanged" :suppressColumnVirtualisation="true"
          @paste-start="handleBulkMutationStart" @paste-end="handleBulkMutationEnd"
          @fill-start="handleBulkMutationStart" @fill-end="handleBulkMutationEnd"
          :suppressRowVirtualisation="true" 
         
       :cellSelection="{ handle: { mode: 'range' } }" :enableClipboard="true"
  
  
          :localeText="AG_GRID_LOCALE_CN" :tooltipShowDelay="500" :headerHeight="50"
          :processCellForClipboard="processCellForClipboard" :processCellFromClipboard="processCellFromClipboard"
          :undoRedoCellEditing="true" :undoRedoCellEditingLimit="20" />
      </div>
      <MethodUnavailableNotice
        v-else
        :title="t('workloadPricing.unavailableTitle')"
        :message="t('workloadPricing.unavailableMessage')"
      />
    </div>
  </div>
</template>
