<script setup lang="ts">
import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef, ColGroupDef } from 'ag-grid-community'
import localforage from 'localforage'
import { getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
import { formatThousands } from '@/lib/numberFormat'
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'
import { loadConsultCategoryFactorMap, loadMajorFactorMap } from '@/lib/xmFactorDefaults'
import { parseNumberOrNull } from '@/lib/number'
import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
import { Button } from '@/components/ui/button'
import {
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogRoot,
  AlertDialogTitle,
  AlertDialogTrigger,
} from 'reka-ui'

import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale';
// 精简的边框配置（细线条+浅灰色，弱化分割线视觉）

interface DictLeaf {
  id: string
  code: string
  name: string
  hasCost: boolean
  hasArea: boolean
}

interface DictGroup {
  id: string
  code: string
  name: string
  children: DictLeaf[]
}

interface DetailRow {
  id: string
  groupCode: string
  groupName: string
  majorCode: string
  majorName: string
  hasCost: boolean
  hasArea: boolean
  amount: number | null
  landArea: number | null
  benchmarkBudget: number | null
  benchmarkBudgetBasic: number | null
  benchmarkBudgetOptional: number | null
  basicFormula: string
  optionalFormula: string
  consultCategoryFactor: number | null
  majorFactor: number | null
  budgetFee: number | null
  budgetFeeBasic: number | null
  budgetFeeOptional: number | null
  remark: string
  path: string[]
}

interface XmInfoState {
  projectName: string
  detailRows: DetailRow[]
}
interface XmBaseInfoState {
  projectIndustry?: string
}

const props = defineProps<{
  contractId: string,
  serviceId: string | number
}>()
const DB_KEY = computed(() => `ydGMF-${props.contractId}-${props.serviceId}`)
const HT_DB_KEY = computed(() => `ht-info-v3-${props.contractId}`)
const BASE_INFO_KEY = 'xm-base-info-v1'
const activeIndustryCode = ref('')
const PRICING_CLEAR_SKIP_PREFIX = 'pricing-clear-skip:'
const PRICING_FORCE_DEFAULT_PREFIX = 'pricing-force-default:'
const pricingPaneReloadStore = usePricingPaneReloadStore()
const consultCategoryFactorMap = ref<Map<string, number | null>>(new Map())
const majorFactorMap = ref<Map<string, number | null>>(new Map())
let factorDefaultsLoaded = false
const paneInstanceCreatedAt = Date.now()

const detailRows = ref<DetailRow[]>([])
const getDefaultConsultCategoryFactor = () =>
  consultCategoryFactorMap.value.get(String(props.serviceId)) ?? null

const getDefaultMajorFactorById = (id: string): number | null => majorFactorMap.value.get(id) ?? null

const loadFactorDefaults = async () => {
  const [consultMap, majorMap] = await Promise.all([
    loadConsultCategoryFactorMap(),
    loadMajorFactorMap()
  ])
  consultCategoryFactorMap.value = consultMap
  majorFactorMap.value = majorMap
  factorDefaultsLoaded = true
}

const ensureFactorDefaultsLoaded = async () => {
  if (factorDefaultsLoaded) return
  await loadFactorDefaults()
}

const shouldForceDefaultLoad = () => {
  const storageKey = `${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
}

const shouldSkipPersist = () => {
  const storageKey = `${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
}

type majorLite = { code: string; name: string; defCoe: number | null; hasCost?: boolean; hasArea?: boolean }
const serviceEntries = getMajorDictEntries().map(({ id, item }) => [id, item] as [string, majorLite])
const majorIdAliasMap = new Map(getMajorDictEntries().map(({ rawId, id }) => [rawId, id]))

const detailDict: DictGroup[] = (() => {
  const groupMap = new Map<string, DictGroup>()
  const groupOrder: string[] = []
  const codeLookup = new Map(serviceEntries.map(([key, item]) => [item.code, { id: key, code: item.code, name: item.name }]))

  for (const [key, item] of serviceEntries) {
    const code = item.code
    const isGroup = !code.includes('-')
    if (isGroup) {
      if (!groupMap.has(code)) groupOrder.push(code)
      groupMap.set(code, {
        id: key,
        code,
        name: item.name,
        children: []
      })
      continue
    }

    const parentCode = code.split('-')[0]
    if (!groupMap.has(parentCode)) {
      const parent = codeLookup.get(parentCode)
      if (!groupOrder.includes(parentCode)) groupOrder.push(parentCode)
      groupMap.set(parentCode, {
        id: parent?.id || `group-${parentCode}`,
        code: parentCode,
        name: parent?.name || parentCode,
        children: []
      })
    }

    groupMap.get(parentCode)!.children.push({
      id: key,
      code,
      name: item.name,
      hasCost: item.hasCost !== false,
      hasArea: item.hasArea !== false
    })
  }

  return groupOrder.map(code => groupMap.get(code)).filter((group): group is DictGroup => Boolean(group))
})()

const idLabelMap = new Map<string, string>()
for (const group of detailDict) {
  idLabelMap.set(group.id, `${group.code} ${group.name}`)
  for (const child of group.children) {
    idLabelMap.set(child.id, `${child.code} ${child.name}`)
  }
}

const buildDefaultRows = (): DetailRow[] => {
  if (!activeIndustryCode.value) return []
  const rows: DetailRow[] = []
  for (const group of detailDict) {
    if (activeIndustryCode.value && !isMajorIdInIndustryScope(group.id, activeIndustryCode.value)) continue
    for (const child of group.children) {
      rows.push({
        id: child.id,
        groupCode: group.code,
        groupName: group.name,
        majorCode: child.code,
        majorName: child.name,
        hasCost: child.hasCost,
        hasArea: child.hasArea,
        amount: null,
        landArea: null,
        benchmarkBudget: null,
        benchmarkBudgetBasic: null,
        benchmarkBudgetOptional: null,
        basicFormula: '',
        optionalFormula: '',
        consultCategoryFactor: null,
        majorFactor: null,
        budgetFee: null,
        budgetFeeBasic: null,
        budgetFeeOptional: null,
        remark: '',
        path: [group.id, child.id]
      })
    }
  }
  return rows
}

type SourceRow = Pick<DetailRow, 'id'> &
  Partial<
    Pick<
      DetailRow,
      | 'amount'
      | 'landArea'
      | 'benchmarkBudget'
      | 'benchmarkBudgetBasic'
      | 'benchmarkBudgetOptional'
      | 'basicFormula'
      | 'optionalFormula'
      | 'consultCategoryFactor'
      | 'majorFactor'
      | 'budgetFee'
      | 'budgetFeeBasic'
      | 'budgetFeeOptional'
      | 'remark'
    >
  >
const mergeWithDictRows = (
  rowsFromDb: SourceRow[] | undefined,
  options?: { includeScaleValues?: boolean; includeFactorValues?: boolean }
): DetailRow[] => {
  const includeScaleValues = options?.includeScaleValues ?? true
  const includeFactorValues = options?.includeFactorValues ?? true
  const dbValueMap = new Map<string, SourceRow>()
  for (const row of rowsFromDb || []) {
    const rowId = String(row.id)
    dbValueMap.set(rowId, row)
    const aliasId = majorIdAliasMap.get(rowId)
    if (aliasId && !dbValueMap.has(aliasId)) {
      dbValueMap.set(aliasId, row)
    }
  }

  return buildDefaultRows().map(row => {
    const fromDb = dbValueMap.get(row.id)
    if (!fromDb) return row
    const hasConsultCategoryFactor = Object.prototype.hasOwnProperty.call(fromDb, 'consultCategoryFactor')
    const hasMajorFactor = Object.prototype.hasOwnProperty.call(fromDb, 'majorFactor')

    return {
      ...row,
      amount: includeScaleValues && row.hasCost && typeof fromDb.amount === 'number' ? fromDb.amount : null,
      landArea: includeScaleValues && row.hasArea && typeof fromDb.landArea === 'number' ? fromDb.landArea : null,
      benchmarkBudget: typeof fromDb.benchmarkBudget === 'number' ? fromDb.benchmarkBudget : null,
      benchmarkBudgetBasic: typeof fromDb.benchmarkBudgetBasic === 'number' ? fromDb.benchmarkBudgetBasic : null,
      benchmarkBudgetOptional: typeof fromDb.benchmarkBudgetOptional === 'number' ? fromDb.benchmarkBudgetOptional : null,
      basicFormula: typeof fromDb.basicFormula === 'string' ? fromDb.basicFormula : '',
      optionalFormula: typeof fromDb.optionalFormula === 'string' ? fromDb.optionalFormula : '',
      consultCategoryFactor:
        !includeFactorValues
          ? null
          : typeof fromDb.consultCategoryFactor === 'number'
            ? fromDb.consultCategoryFactor
            : hasConsultCategoryFactor
              ? null
              : getDefaultConsultCategoryFactor(),
      majorFactor:
        !includeFactorValues
          ? null
          : typeof fromDb.majorFactor === 'number'
            ? fromDb.majorFactor
            : hasMajorFactor
              ? null
              : getDefaultMajorFactorById(row.id),
      budgetFee: typeof fromDb.budgetFee === 'number' ? fromDb.budgetFee : null,
      budgetFeeBasic: typeof fromDb.budgetFeeBasic === 'number' ? fromDb.budgetFeeBasic : null,
      budgetFeeOptional: typeof fromDb.budgetFeeOptional === 'number' ? fromDb.budgetFeeOptional : null,
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : ''
    }
  })
}

const formatEditableNumber = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return '请输入'
  }
  if (params.value == null) return ''
  return Number(params.value).toFixed(2)
}

const formatConsultCategoryFactor = (params: any) => {
  return formatEditableNumber(params)
}

const formatMajorFactor = (params: any) => {
  return formatEditableNumber(params)
}

const formatReadonlyMoney = (params: any) => {
  if (params.value == null || params.value === '') return ''
  return formatThousands(roundTo(params.value, 2))
}

const getBenchmarkBudgetSplitByLandArea = (row?: Pick<DetailRow, 'landArea'>) =>
  getBenchmarkBudgetSplitByScale(row?.landArea, 'area')

const getBudgetFee = (row?: Pick<DetailRow, 'landArea' | 'majorFactor' | 'consultCategoryFactor'>) => {
  const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
  if (!benchmarkBudgetSplit) return null

  const splitBudgetFee = getScaleBudgetFeeSplit({
    benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
    benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
    majorFactor: row?.majorFactor,
    consultCategoryFactor: row?.consultCategoryFactor
  })
  return splitBudgetFee ? splitBudgetFee.total : null
}

const getBudgetFeeSplit = (row?: Pick<DetailRow, 'landArea' | 'majorFactor' | 'consultCategoryFactor'>) => {
  const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
  if (!benchmarkBudgetSplit) return null
  return getScaleBudgetFeeSplit({
    benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
    benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
    majorFactor: row?.majorFactor,
    consultCategoryFactor: row?.consultCategoryFactor
  })
}

const formatEditableFlexibleNumber = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasArea) {
    return ''
  }
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return '点击输入'
  }
  if (params.value == null) return ''
  return formatThousands(params.value, 3)
}

const columnDefs: Array<ColDef<DetailRow> | ColGroupDef<DetailRow>> = [
  {
    headerName: '用地面积(亩)',
    field: 'landArea',
    headerClass: 'ag-right-aligned-header',
    minWidth: 170,
    flex: 1,

    editable: params => !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea),
    cellClass: params =>
      !params.node?.group && !params.node?.rowPinned && params.data?.hasArea
        ? 'ag-right-aligned-cell editable-cell-line'
        : 'ag-right-aligned-cell',
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea) && (params.value == null || params.value === '')
    },
    valueParser: params => {
      const value = parseNumberOrNull(params.newValue)
      return value == null ? null : roundTo(value, 3)
    },
    valueFormatter: formatEditableFlexibleNumber
  },
  
  {
    headerName: '咨询分类系数',
    field: 'consultCategoryFactor',
    width: 80,
    minWidth: 70,
    maxWidth: 90,
    editable: params => !params.node?.group && !params.node?.rowPinned,
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
    },
    valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
    valueFormatter: formatConsultCategoryFactor
  },
  {
    headerName: '专业系数',
    field: 'majorFactor',
    width: 80,
    minWidth: 70,
    maxWidth: 90,
    editable: params => !params.node?.group && !params.node?.rowPinned,
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
    },
    valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
    valueFormatter: formatMajorFactor
  },
  {
    headerName: '基准预算(元)',
    marryChildren: true,
    children: [
      {
        headerName: '基本工作',
        field: 'benchmarkBudgetBasic',
        colId: 'benchmarkBudgetBasic',
        headerClass: 'ag-right-aligned-header',
        minWidth: 140,
        flex: 1,
        cellClass: 'ag-right-aligned-cell',
        aggFunc: decimalAggSum,
        valueGetter: params =>
          params.node?.rowPinned
            ? params.data?.benchmarkBudgetBasic ?? null
            : getBenchmarkBudgetSplitByLandArea(params.data)?.basic ?? null,
        valueFormatter: formatReadonlyMoney
      },
      {
        headerName: '可选工作',
        field: 'benchmarkBudgetOptional',
        colId: 'benchmarkBudgetOptional',
        headerClass: 'ag-right-aligned-header',
        minWidth: 140,
        flex: 1,
        cellClass: 'ag-right-aligned-cell',
        aggFunc: decimalAggSum,
        valueGetter: params =>
          params.node?.rowPinned
            ? params.data?.benchmarkBudgetOptional ?? null
            : getBenchmarkBudgetSplitByLandArea(params.data)?.optional ?? null,
        valueFormatter: formatReadonlyMoney
      }
    ]
  },
  {
    headerName: '预算费用',
    marryChildren: true,
    children: [
      {
        headerName: '基本工作',
        field: 'budgetFeeBasic',
        colId: 'budgetFeeBasic',
        headerClass: 'ag-right-aligned-header',
        minWidth: 130,
        flex: 1,
        cellClass: 'ag-right-aligned-cell',
        aggFunc: decimalAggSum,
        valueGetter: params =>
          params.node?.rowPinned
            ? params.data?.budgetFeeBasic ?? null
            : getBudgetFeeSplit(params.data)?.basic ?? null,
        valueFormatter: formatReadonlyMoney
      },
      {
        headerName: '可选工作',
        field: 'budgetFeeOptional',
        colId: 'budgetFeeOptional',
        headerClass: 'ag-right-aligned-header',
        minWidth: 130,
        flex: 1,
        cellClass: 'ag-right-aligned-cell',
        aggFunc: decimalAggSum,
        valueGetter: params =>
          params.node?.rowPinned
            ? params.data?.budgetFeeOptional ?? null
            : getBudgetFeeSplit(params.data)?.optional ?? null,
        valueFormatter: formatReadonlyMoney
      },
      {
        headerName: '合计',
        field: 'budgetFee',
        colId: 'budgetFeeTotal',
        headerClass: 'ag-right-aligned-header',
        minWidth: 140,
        flex: 1,
        cellClass: 'ag-right-aligned-cell',
        aggFunc: decimalAggSum,
        valueGetter: params => (params.node?.rowPinned ? params.data?.budgetFee ?? null : getBudgetFee(params.data)),
        valueFormatter: formatReadonlyMoney
      }
    ]
  },
  {
    headerName: '说明',
    field: 'remark',
    minWidth: 180,
    flex: 1.2,
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },

    editable: params => !params.node?.group && !params.node?.rowPinned,
    valueFormatter: params => {
      if (!params.node?.group && !params.node?.rowPinned && !params.value) return '点击输入'
      return params.value || ''
    },
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line remark-wrap-cell' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
    }
  }
]

const autoGroupColumnDef: ColDef = {
  headerName: '专业编码以及工程专业名称',
  minWidth: 320,
  pinned: 'left',
  flex: 2,

  cellRendererParams: {
    suppressCount: true
  },
  valueFormatter: params => {
    if (params.node?.rowPinned) {
      return '总合计'
    }
    const nodeId = String(params.value || '')
    return idLabelMap.get(nodeId) || nodeId
  },
  tooltipValueGetter: params => {
    if (params.node?.rowPinned) return '总合计'
    const nodeId = String(params.value || '')
    return idLabelMap.get(nodeId) || nodeId
  }
}



const totalAmount = computed(() => sumByNumber(detailRows.value, row => row.amount))

const totalBenchmarkBudgetBasic = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByLandArea(row)?.basic))
const totalBenchmarkBudgetOptional = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByLandArea(row)?.optional))
const totalBenchmarkBudget = computed(() => sumByNumber(detailRows.value, row => getBenchmarkBudgetSplitByLandArea(row)?.total))

const totalBudgetFeeBasic = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.basic))
const totalBudgetFeeOptional = computed(() => sumByNumber(detailRows.value, row => getBudgetFeeSplit(row)?.optional))
const totalBudgetFee = computed(() => sumByNumber(detailRows.value, row => getBudgetFee(row)))
const pinnedTopRowData = computed(() => [
  {
    id: 'pinned-total-row',
    groupCode: '',
    groupName: '',
    majorCode: '',
    majorName: '',
    hasCost: false,
    hasArea: false,
    amount: totalAmount.value,
    landArea: null,
    benchmarkBudget: totalBenchmarkBudget.value,
    benchmarkBudgetBasic: totalBenchmarkBudgetBasic.value,
    benchmarkBudgetOptional: totalBenchmarkBudgetOptional.value,
    basicFormula: '',
    optionalFormula: '',
    consultCategoryFactor: null,
    majorFactor: null,
    budgetFee: totalBudgetFee.value,
    budgetFeeBasic: totalBudgetFeeBasic.value,
    budgetFeeOptional: totalBudgetFeeOptional.value,
    remark: '',
    path: ['TOTAL']
  }
])

const buildPersistDetailRows = () =>
  detailRows.value.map(row => {
    const benchmarkBudgetSplit = getBenchmarkBudgetSplitByLandArea(row)
    const budgetFeeSplit = benchmarkBudgetSplit
      ? getScaleBudgetFeeSplit({
          benchmarkBudgetBasic: benchmarkBudgetSplit.basic,
          benchmarkBudgetOptional: benchmarkBudgetSplit.optional,
          majorFactor: row.majorFactor,
          consultCategoryFactor: row.consultCategoryFactor
        })
      : null

    return {
      ...row,
      benchmarkBudget: benchmarkBudgetSplit?.total ?? null,
      benchmarkBudgetBasic: benchmarkBudgetSplit?.basic ?? null,
      benchmarkBudgetOptional: benchmarkBudgetSplit?.optional ?? null,
      basicFormula: benchmarkBudgetSplit?.basicFormula ?? '',
      optionalFormula: benchmarkBudgetSplit?.optionalFormula ?? '',
      budgetFee: budgetFeeSplit?.total ?? null,
      budgetFeeBasic: budgetFeeSplit?.basic ?? null,
      budgetFeeOptional: budgetFeeSplit?.optional ?? null
    }
  })

const saveToIndexedDB = async () => {
  if (shouldSkipPersist()) return
  try {
    const payload = {
      detailRows: JSON.parse(JSON.stringify(buildPersistDetailRows()))
    }
    console.log('Saving to IndexedDB:', payload)
    await localforage.setItem(DB_KEY.value, payload)
    const synced = await syncPricingTotalToZxFw({
      contractId: props.contractId,
      serviceId: props.serviceId,
      field: 'landScale',
      value: totalBudgetFee.value
    })
    if (synced) {
      pricingPaneReloadStore.markReload(props.contractId, ZXFW_RELOAD_SERVICE_KEY)
    }
  } catch (error) {
    console.error('saveToIndexedDB failed:', error)
  }
}

const loadFromIndexedDB = async () => {
  try {
    const baseInfo = await localforage.getItem<XmBaseInfoState>(BASE_INFO_KEY)
    activeIndustryCode.value =
      typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''

    await ensureFactorDefaultsLoaded()
    if (shouldForceDefaultLoad()) {
      const htData = await localforage.getItem<{ detailRows: SourceRow[] }>(HT_DB_KEY.value)
      detailRows.value = htData?.detailRows
        ? mergeWithDictRows(htData.detailRows, { includeScaleValues: false, includeFactorValues: false })
        : buildDefaultRows()
      return
    }

    const data = await localforage.getItem<XmInfoState>(DB_KEY.value)
    if (data) {
      detailRows.value = mergeWithDictRows(data.detailRows)
      return
    }

    const htData = await localforage.getItem<{ detailRows: SourceRow[] }>(HT_DB_KEY.value)
    if (htData?.detailRows) {
      detailRows.value = mergeWithDictRows(htData.detailRows, { includeScaleValues: false, includeFactorValues: false })
      return
    }

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

const importContractData = async () => {
  try {
    const baseInfo = await localforage.getItem<XmBaseInfoState>(BASE_INFO_KEY)
    activeIndustryCode.value =
      typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''

    // 使用默认数据时，强制读取最新的项目系数（预算取值优先，空值回退标准系数）
    await loadFactorDefaults()
    const htData = await localforage.getItem<{ detailRows: SourceRow[] }>(HT_DB_KEY.value)
    const hasContractRows = Array.isArray(htData?.detailRows) && htData.detailRows.length > 0
    detailRows.value = hasContractRows
      ? mergeWithDictRows(htData!.detailRows, { includeFactorValues: true })
      : buildDefaultRows().map(row => ({
          ...row,
          consultCategoryFactor: getDefaultConsultCategoryFactor(),
          majorFactor: getDefaultMajorFactorById(row.id)
        }))
    await saveToIndexedDB()
  } catch (error) {
    console.error('importContractData failed:', error)
  }
}

watch(
  () => pricingPaneReloadStore.getReloadVersion(props.contractId, props.serviceId),
  (nextVersion, prevVersion) => {
    if (nextVersion === prevVersion || nextVersion === 0) return
    void loadFromIndexedDB()
  }
)

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




let gridPersistTimer: ReturnType<typeof setTimeout> | null = null
const handleCellValueChanged = () => {
  if (gridPersistTimer) clearTimeout(gridPersistTimer)
  gridPersistTimer = setTimeout(() => {
    void saveToIndexedDB()
  }, 1000)
}

onMounted(async () => {
  await loadFromIndexedDB()
})

onActivated(() => {
  void loadFromIndexedDB()
})

onBeforeUnmount(() => {
  if (persistTimer) clearTimeout(persistTimer)
  if (gridPersistTimer) clearTimeout(gridPersistTimer)
  void saveToIndexedDB()
})
const processCellForClipboard = (params: any) => {
  if (Array.isArray(params.value)) {
    return JSON.stringify(params.value); // 数组转字符串复制
  }
  return params.value;
};

const processCellFromClipboard = (params: any) => {
  try {
    const parsed = JSON.parse(params.value);
    if (Array.isArray(parsed)) return parsed;
  } catch (e) {
    // 解析失败时返回原始值，无需额外处理
  }
  return params.value;
};


</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">用地规模明细</h3>
        <AlertDialogRoot>
          <AlertDialogTrigger as-child>
            <Button type="button" variant="outline" size="sm">使用默认数据</Button>
          </AlertDialogTrigger>
          <AlertDialogPortal>
            <AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
            <AlertDialogContent class="fixed left-1/2 top-1/2 z-50 w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
              <AlertDialogTitle class="text-base font-semibold">确认覆盖当前明细</AlertDialogTitle>
              <AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
                将使用合同默认数据覆盖当前用地规模明细，是否继续？
              </AlertDialogDescription>
              <div class="mt-4 flex items-center justify-end gap-2">
                <AlertDialogCancel as-child>
                  <Button variant="outline">取消</Button>
                </AlertDialogCancel>
                <AlertDialogAction as-child>
                  <Button @click="importContractData">确认覆盖</Button>
                </AlertDialogAction>
              </div>
            </AlertDialogContent>
          </AlertDialogPortal>
        </AlertDialogRoot>
      </div>

      <div class="ag-theme-quartz h-full min-h-0 w-full flex-1">
        <AgGridVue :style="{ height: '100%' }" :rowData="detailRows" :pinnedTopRowData="pinnedTopRowData"
          :columnDefs="columnDefs" :autoGroupColumnDef="autoGroupColumnDef" :gridOptions="gridOptions" :theme="myTheme"
          @cell-value-changed="handleCellValueChanged" :suppressColumnVirtualisation="true"
          :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>
    </div>
  </div>
</template>


