<script setup lang="ts">
import {computed, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch} from 'vue'
import {useI18n} from 'vue-i18n'
import {AgGridVue} from 'ag-grid-vue3'
import type {
  ColDef,
  ColGroupDef,
  FirstDataRenderedEvent,
  GridApi,
  GridReadyEvent,
  RowDataUpdatedEvent
} from 'ag-grid-community'
import {expertList} from '@/sql'
import {myTheme, gridOptions, agGridWrapClass, agGridStyle} from '@/lib/diyAgGridOptions'
import {decimalAggSum, roundTo, sumByNumber, sumNullableNumbers, toDecimal} from '@/lib/decimal'
import {formatThousandsFlexible} from '@/lib/numberFormat'
import {parseNumberOrNull} from '@/lib/number'
import {syncPricingTotalToZxFw, type ZxFwPricingField} from '@/lib/zxFwPricingSync'
import {useZxFwPricingStore, type HtFeeMethodType, type ServicePricingMethod} from '@/pinia/zxFwPricing'
import {AG_GRID_LOCALE_CN} from '@ag-grid-community/locale'
import {buildProjectScopedSessionKey} from '@/lib/pricingPersistControl'
import {withReadonlyAutoHeight} from '@/lib/agGridReadonlyAutoHeight'
import {formatScaleEditableNumber, formatScaleReadonlyMoney} from "@/lib/pricingScaleGrid";
import {ValueParserParams, ValueFormatterParams} from 'ag-grid-community';
import {useKvStore} from "@/pinia/kv";
import {useDataStore} from '@/pinia/zx'
/**
 * 数据行接口定义
 * 注意：此接口需与 columnDefs 中的字段保持一致
 */
interface DetailRow {
  id: string
  expertCode: string
  expertName: string
  laborBudgetUnitPrice: string
  compositeBudgetUnitPrice: string
  adoptedBudgetUnitPrice: number | null
  personnelCount: number | null
  workdayCount: number | string  | null
  serviceBudget: number | string  | null
  remark: string
  path: string[]
  // 新增：按职称分类的工时字段（与 columnDefs 对应）
  unitPrice?: number | string | null
  workdayCount1?: number | string  | null
  feeSubtotal?: number | string  | null
  unitPrice2?: number | string | null
  workdayCount2?: number | string  | null
  feeSubtotal2?: number  | string | null
  unitPrice3?: number | string | null
  workdayCount3?: number | string  | null
  feeSubtotal3?: number | string  | null
  unitPrice4?: number | string | null
  workdayCount4?: number | string  | null
  feeSubtotal4?: number | string  | null
  unitPrice5?: number | string | null
  workdayCount5?: number | string  | null
  feeSubtotal5?: number | string  | null
  workdayCount6?: number | string | null
  feeSubtotal6?: number | string  | null,
  avgUnitPrice: number | string  | null
}

interface GridState {
  detailRows: DetailRow[]
}

const props = withDefaults(
    defineProps<{
      storageKey: string
      title?: string
      contractId?: string
      serviceId?: string | number
      enableZxFwSync?: boolean
      syncField?: ZxFwPricingField
      htMainStorageKey?: string
      htRowId?: string
      htMethodType?: HtFeeMethodType
    }>(),
    {
      title: undefined,
      enableZxFwSync: false,
      syncField: 'hourly'
    }
)
const zxFwPricingStore = useZxFwPricingStore()
const {t, locale} = useI18n()

const PRICING_CLEAR_SKIP_PREFIX = 'pricing-clear-skip:'
const PRICING_FORCE_DEFAULT_PREFIX = 'pricing-force-default:'
const paneInstanceCreatedAt = Date.now()

const shouldSkipPersist = () => {
  const storageKey = buildProjectScopedSessionKey(PRICING_CLEAR_SKIP_PREFIX, props.storageKey)
  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
}

const shouldForceDefaultLoad = () => {
  const storageKey = buildProjectScopedSessionKey(PRICING_FORCE_DEFAULT_PREFIX, props.storageKey)
  const raw = sessionStorage.getItem(storageKey)
  if (!raw) return false
  const forceUntil = Number(raw)
  sessionStorage.removeItem(storageKey)
  return Number.isFinite(forceUntil) && Date.now() <= forceUntil
}

const fallbackDetailRows = ref<DetailRow[]>([])
const gridApi = ref<GridApi<DetailRow> | null>(null)
const serviceMethod = computed<ServicePricingMethod | null>(() => {
  if (props.syncField === 'investScale') return 'investScale'
  if (props.syncField === 'landScale') return 'landScale'
  if (props.syncField === 'workload') return 'workload'
  if (props.syncField === 'hourly') return 'hourly'
  return null
})
const useServicePricingState = computed(
    () => Boolean(props.enableZxFwSync && props.contractId && props.serviceId != null && serviceMethod.value)
)
const useHtMethodState = computed(
    () => Boolean(props.htMainStorageKey && props.htRowId && props.htMethodType)
)
const getServiceMethodState = () => {
  if (!useServicePricingState.value || !serviceMethod.value) return null
  return zxFwPricingStore.getServicePricingMethodState<DetailRow>(props.contractId!, props.serviceId!, serviceMethod.value)
}
const getHtMethodState = () => {
  if (!useHtMethodState.value) return null
  return zxFwPricingStore.getHtFeeMethodState<GridState>(
      props.htMainStorageKey!,
      props.htRowId!,
      props.htMethodType!
  )
}
const detailRows = computed<DetailRow[]>({
  get: () => {
    return fallbackDetailRows.value
  },
  set: rows => {
    fallbackDetailRows.value = rows
  }
})

type ExpertLite = {
  code: string
  name: string
  maxPrice: number | null
  minPrice: number | null
  defPrice: number | null
  manageCoe: number | null
}

const getExpertDisplayName = (expert: ExpertLite | undefined) => {
  if (!expert) return ''
  return String(locale.value).toLowerCase().startsWith('en')
      ? (expert as ExpertLite & { nameEn?: string }).nameEn || expert.name
      : expert.name
}

const expertEntries = Object.entries(expertList as Record<string, ExpertLite>)
    .sort((a, b) => Number(a[0]) - Number(b[0]))
    .filter((entry): entry is [string, ExpertLite] => {
      const item = entry[1]
      return Boolean(item?.code && item?.name)
    })

const formatPriceRange = (min: number | null, max: number | null) => {
  const hasMin = typeof min === 'number' && Number.isFinite(min)
  const hasMax = typeof max === 'number' && Number.isFinite(max)
  if (hasMin && hasMax) return `${min}-${max}`
  if (hasMin) return String(min)
  if (hasMax) return String(max)
  return ''
}

const getCompositeBudgetUnitPriceRange = (expert: ExpertLite) => {
  if (typeof expert.manageCoe !== 'number' || !Number.isFinite(expert.manageCoe)) return ''
  const min =
      typeof expert.minPrice === 'number' && Number.isFinite(expert.minPrice)
          ? roundTo(toDecimal(expert.minPrice).mul(expert.manageCoe), 2)
          : null
  const max =
      typeof expert.maxPrice === 'number' && Number.isFinite(expert.maxPrice)
          ? roundTo(toDecimal(expert.maxPrice).mul(expert.manageCoe), 2)
          : null
  return formatPriceRange(min, max)
}

const getDefaultAdoptedBudgetUnitPrice = (expert: ExpertLite) => {
  if (
      typeof expert.defPrice !== 'number' ||
      !Number.isFinite(expert.defPrice) ||
      typeof expert.manageCoe !== 'number' ||
      !Number.isFinite(expert.manageCoe)
  ) {
    return null
  }
  return roundTo(toDecimal(expert.defPrice).mul(expert.manageCoe), 2)
}

const buildDefaultRows = async (): Promise<DetailRow[]> => {
  const rows: DetailRow[] = []
  const rowsToMap = await useDataStore().query([
    { field: 'type', value: `${props.contractId}-zxFw`, operator: 'eq' }
  ])
  for (const expert of rowsToMap) {
    const expertId = expert.id || expert._id // 根据实际字段调整
    const contractId = props.contractId
    const rowId = `${contractId}-hourly-${expertId}`
    rows.push({
      id: rowId,
      type: `${props.contractId}-hourly`,
      expertCode: expert.code,
      expertName: getExpertDisplayName(expert),
      laborBudgetUnitPrice: formatPriceRange(expert.minPrice, expert.maxPrice),
      compositeBudgetUnitPrice: getCompositeBudgetUnitPriceRange(expert),
      adoptedBudgetUnitPrice: getDefaultAdoptedBudgetUnitPrice(expert),
      personnelCount: null,
      workdayCount: null,
      serviceBudget: null,
      remark: '',
      path: [rowId]
    })
  }
  return rows
}

/**
 * 合并数据库中的行数据与默认行数据
 * 保留用户编辑的所有字段值（包括新增的职称分类字段）
 */
const mergeWithDictRows = async (rowsFromDb: DetailRow[] | undefined): Promise<DetailRow[]> => {
  const dbValueMap = new Map<string, DetailRow>()
  for (const row of rowsFromDb || []) {
    dbValueMap.set(row.id, row)
  }

  const defaultRows = await buildDefaultRows()
  return defaultRows.map(row => {
    const fromDb = dbValueMap.get(row.id)
    if (!fromDb) return row

    // 使用展开运算符合并所有字段，确保新增字段也能正确合并
    return {
      ...row,
      // 保留数据库中的所有可编辑字段
      adoptedBudgetUnitPrice: typeof fromDb.adoptedBudgetUnitPrice === 'number' ? fromDb.adoptedBudgetUnitPrice : null,
      personnelCount: typeof fromDb.personnelCount === 'number' ? fromDb.personnelCount : null,
      workdayCount: typeof fromDb.workdayCount === 'number' ? fromDb.workdayCount : null,
      serviceBudget: typeof fromDb.serviceBudget === 'number' ? fromDb.serviceBudget : null,
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : '',
      // 合并新增的职称分类字段
      unitPrice: typeof fromDb.unitPrice === 'number' ? fromDb.unitPrice : null,
      workdayCount1: typeof fromDb.workdayCount1 === 'number' ? fromDb.workdayCount1 : null,
      feeSubtotal: typeof fromDb.feeSubtotal === 'number' ? fromDb.feeSubtotal : null,
      unitPrice2: typeof fromDb.unitPrice2 === 'number' ? fromDb.unitPrice2 : null,
      workdayCount2: typeof fromDb.workdayCount2 === 'number' ? fromDb.workdayCount2 : null,
      feeSubtotal2: typeof fromDb.feeSubtotal2 === 'number' ? fromDb.feeSubtotal2 : null,
      unitPrice3: typeof fromDb.unitPrice3 === 'number' ? fromDb.unitPrice3 : null,
      workdayCount3: typeof fromDb.workdayCount3 === 'number' ? fromDb.workdayCount3 : null,
      feeSubtotal3: typeof fromDb.feeSubtotal3 === 'number' ? fromDb.feeSubtotal3 : null,
      unitPrice4: typeof fromDb.unitPrice4 === 'number' ? fromDb.unitPrice4 : null,
      workdayCount4: typeof fromDb.workdayCount4 === 'number' ? fromDb.workdayCount4 : null,
      feeSubtotal4: typeof fromDb.feeSubtotal4 === 'number' ? fromDb.feeSubtotal4 : null,
      unitPrice5: typeof fromDb.unitPrice5 === 'number' ? fromDb.unitPrice5 : null,
      workdayCount5: typeof fromDb.workdayCount5 === 'number' ? fromDb.workdayCount5 : null,
      feeSubtotal5: typeof fromDb.feeSubtotal5 === 'number' ? fromDb.feeSubtotal5 : null,
      feeSubtotal6: typeof fromDb.feeSubtotal6 === 'number' ? fromDb.feeSubtotal6 : null,
      avgUnitPrice: typeof fromDb.avgUnitPrice === 'number' ? fromDb.avgUnitPrice : null,
    }
  })
}

const parseNonNegativeIntegerOrNull = (value: unknown) => {
  if (value === '' || value == null) return null
  const parsed = parseNumberOrNull(value, {sanitize: true, precision: 0})
  if (parsed == null) return null
  if (!Number.isSafeInteger(parsed) || parsed < 0) return null
  return parsed
}

const formatEditableNumber = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return t('hourlyFeeGrid.clickToInput')
  }
  if (params.value == null) return ''
  return formatThousandsFlexible(params.value, 3)
}

const formatEditableInteger = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return t('hourlyFeeGrid.clickToInput')
  }
  if (params.value == null) return ''
  return String(Number(params.value))
}

const calcServiceBudget = (row: DetailRow | undefined) => {
  const adopted = row?.adoptedBudgetUnitPrice
  const personnel = row?.personnelCount
  const workday = row?.workdayCount
  if (
      typeof adopted !== 'number' ||
      !Number.isFinite(adopted) ||
      typeof personnel !== 'number' ||
      !Number.isFinite(personnel) ||
      typeof workday !== 'number' ||
      !Number.isFinite(workday)
  ) {
    return null
  }
  return roundTo(toDecimal(adopted).mul(personnel).mul(workday), 2)
}

const syncServiceBudgetToRows = () => {
  for (const row of detailRows.value) {
    // row.serviceBudget = calcServiceBudget(row)

    // 计算各职称的费用小计（单价 × 工日）
    row.feeSubtotal = Math.round((row.unitPrice ?? 0) * (row.workdayCount ?? 0))
    row.feeSubtotal2 = Math.round((row.unitPrice2 ?? 0) * (row.workdayCount2 ?? 0))
    row.feeSubtotal3 = Math.round((row.unitPrice3 ?? 0) * (row.workdayCount3 ?? 0))
    row.feeSubtotal4 = Math.round((row.unitPrice4 ?? 0) * (row.workdayCount4 ?? 0))
    row.feeSubtotal5 = Math.round((row.unitPrice5 ?? 0) * (row.workdayCount5 ?? 0))

    // 计算总工日
    row.workdayCount6 = (row.workdayCount ?? 0) +
        (row.workdayCount2 ?? 0) +
        (row.workdayCount3 ?? 0) +
        (row.workdayCount4 ?? 0) +
        (row.workdayCount5 ?? 0)

    // 计算总费用
    row.feeSubtotal6 = Math.round(
        (row.workdayCount ?? 0) * (row.unitPrice ?? 0) +
        (row.workdayCount2 ?? 0) * (row.unitPrice2 ?? 0) +
        (row.workdayCount3 ?? 0) * (row.unitPrice3 ?? 0) +
        (row.workdayCount4 ?? 0) * (row.unitPrice4 ?? 0) +
        (row.workdayCount5 ?? 0) * (row.unitPrice5 ?? 0)
    )

    // 计算平均单价
    const totalWorkday = row.workdayCount6 ?? 0
    const totalFee = row.feeSubtotal6 ?? 0
    row.avgUnitPrice = Math.round(totalWorkday * totalFee)
  }
}

function editableNumberCol2(
    field: string,
    headerName: string,
    options: {
      decimals?: 0 | 1; // 精度：0=整数，1=1位小数
      aggFunc?: string;
    } = {}
): ColDef {
  const {decimals = 0, aggFunc = 'sum'} = options;

  /*const valueParser = (params: ValueParserParams) => {
    const val = params.newValue?.trim();
    if (!val) return null;
    const num = parseFloat(val);
    return isNaN(num) || num < 0 ? null : num;
  };*/

  const valueFormatter = (params: ValueFormatterParams) => {
    if (params.node?.rowPinned === 'bottom') {
      return '/';
    }
    const val = params.value;
    if (val === null || val === undefined) return 0;
    if (decimals === 0) {
      return Math.round(val).toString(); // 强制整数显示
    } else {
      return Number(val).toFixed(1); // 保留1位小数
    }
  };

  return {
    field,
    headerName,
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    cellDataType: 'number',
    valueFormatter,
    aggFunc,
  };
}

function editableNumberCol3(
    field: string,
    headerName: string,
    options: {
      decimals?: 0 | 1; // 精度：0=整数，1=1位小数
      aggFunc?: string;
    } = {}
): ColDef {
  const {decimals = 0, aggFunc = 'sum'} = options;

  const valueParser = (params: ValueParserParams) => {
    const val = params.newValue?.trim();
    if (!val) return null;
    const num = parseFloat(val);
    return isNaN(num) || num < 0 ? null : num;
  };

  const valueFormatter = (params: ValueFormatterParams) => {
    const val = params.value;
    if (val === null || val === undefined) return 0;
    if (decimals === 0) {
      return Math.round(val).toString(); // 强制整数显示
    } else {
      return Number(val).toFixed(1); // 保留1位小数
    }
  };

  return {
    field,
    headerName,
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    cellDataType: 'number',
    valueParser,
    valueFormatter,
    aggFunc,
  };
}

// 费用小计列（计算列，不可编辑）
function calculatedFeeSubtotalCol(
    field: string,
    headerName: string,
    unitPriceField: string = 'unitPrice',
    workdayCountField: string = 'workdayCount'
): ColDef {
  return {
    field,
    headerName,
    editable: false,
    cellDataType: 'number',
    valueGetter: (params) => {
      const unitPrice = params.data?.[unitPriceField] ?? 0;
      const workdayCount = params.data?.[workdayCountField] ?? 0;
      const subtotal = unitPrice * workdayCount;
      if (params.node?.rowPinned === 'bottom') {
        // 总计行的特殊计算逻辑
        // 例如：使用不同的计算方式或直接返回预计算的值
        const allRows = fallbackDetailRows.value;
        const total = allRows.reduce((sum, row) => {
          return sum + ((row[unitPriceField as keyof DetailRow] as number || 0) *
              (row[workdayCountField as keyof DetailRow] as number || 0));
        }, 0);
        return Math.round(total); // 显示为整数
      }
      return Math.round(subtotal); // 费用小计为整数
    },
    /*valueFormatter: (params: ValueFormatterParams) => {
      const unitPrice = params.data?.[unitPriceField] ?? 0;
      const workdayCount = params.data?.[workdayCountField] ?? 0;
      const subtotal = unitPrice * workdayCount;
      return Math.round(subtotal); // 显示为整数
    },*/
    aggFunc: 'sum'
  };
}

function calculatedFeeSubtotalCol2(
    field: string,
    headerName: string,
    unitPriceField: string = 'unitPrice',
    workdayCountField: string = 'workdayCount'
): ColDef {
  return {
    field,
    headerName,
    editable: false,
    cellDataType: 'number',
    valueGetter: (params) => {
      const unitPrice = params.data?.[unitPriceField] ?? 0;
      const workdayCount = params.data?.[workdayCountField] ?? 0;
      const subtotal = unitPrice * workdayCount;
      return Math.round(subtotal); // 费用小计为整数
    },
    valueFormatter: (params: ValueFormatterParams) => {
      const allRows = fallbackDetailRows.value;
      const total = allRows.reduce((sum, row) => {
        return sum + ((row[unitPriceField as keyof DetailRow] as number || 0) *
            (row[workdayCountField as keyof DetailRow] as number || 0));
      }, 0);
      return Math.round(total); // 显示为整数
    },
    aggFunc: 'sum'
  };
}

function calculatedWorkdayTotalCol(
    field: string,
   headerName: string,
   options: {
     decimals?: 0 | 1; // 精度：0=整数，1=1位小数
     aggFunc?: string;
   }): ColDef {
    const {decimals = 0, aggFunc = 'sum'} = options;
  return {
    field,
    headerName,
    editable: false,
    cellDataType: 'number',
    valueGetter: (params) => {
      const workdayCount1 = params.data?.['workdayCount'] ?? 0;
      const workdayCount2 = params.data?.['workdayCount2'] ?? 0;
      const workdayCount3 = params.data?.['workdayCount3'] ?? 0;
      const workdayCount4 = params.data?.['workdayCount4'] ?? 0;
      const workdayCount5 = params.data?.['workdayCount5'] ?? 0;
      const count = workdayCount1 + workdayCount2 + workdayCount3 + workdayCount4 + workdayCount5;
      return count; // 费用小计为整数
    },
    valueFormatter: (params: ValueFormatterParams) => {
      const val = params.value;
      if (val === null || val === undefined) return '';
      if (decimals === 0) {
        return Math.round(val).toString(); // 强制整数显示
      } else {
        return Number(val).toFixed(1); // 保留1位小数
      }
    }
  }
}

function calculatedAvgUnitPriceCol(
    field: string,
    headerName: string,
    options: {
      decimals?: 0 | 1; // 精度：0=整数，1=1位小数
      aggFunc?: string;
    }): ColDef {
  const {decimals = 0, aggFunc = 'sum'} = options;
  return {
    field,
    headerName,
    editable: false,
    cellDataType: 'number'
  }
}

function calculatedSubTotalCol(
    field: string,
    headerName: string,
    options: {
      decimals?: 0 | 1; // 精度：0=整数，1=1位小数
      aggFunc?: string;
    }): ColDef {
  const {decimals = 0, aggFunc = 'sum'} = options;
  return {
    field,
    headerName,
    editable: false,
    cellDataType: 'number'
  }
}

const editableNumberCol = <K extends keyof DetailRow>(
    field: K,
    headerName: string,
    extra: Partial<ColDef<DetailRow>> = {}
): ColDef<DetailRow> => ({
  headerName,
  field,
  minWidth: 120,
  flex: 1,
  editable: params => !params.node?.group && !params.node?.rowPinned,
  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 && (params.value == null || params.value === '')
  },
  valueParser: params => parseNumberOrNull(params.newValue, {precision: 3}),
  valueFormatter: formatEditableNumber,
  ...extra
})

const editableMoneyCol = <K extends keyof DetailRow>(
    field: K,
    headerName: string,
    extra: Partial<ColDef<DetailRow>> = {}
): ColDef<DetailRow> => ({
  headerName,
  field,
  headerClass: 'ag-right-aligned-header',
  minWidth: 120,
  flex: 1,
  editable: params => !params.node?.group && !params.node?.rowPinned,
  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 && (params.value == null || params.value === '')
  },
  valueParser: params => parseNumberOrNull(params.newValue, {precision: 3}),
  valueFormatter: params => {
    if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
      return t('hourlyFeeGrid.clickToInput')
    }
    if (params.value == null) return ''
    return formatThousandsFlexible(params.value, 3)
  },
  ...extra
})

const readonlyTextCol = <K extends keyof DetailRow>(
    field: K,
    headerName: string,
    extra: Partial<ColDef<DetailRow>> = {}
): ColDef<DetailRow> => ({
  headerName,
  field,
  minWidth: 120,
  flex: 1,
  editable: false,
  valueFormatter: params => params.value || '',
  ...extra
})

const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
  {
    headerName: t('hourlyFeeGrid.columns.code'),
    field: 'expertCode',
    minWidth: 90,
    width: 100,
    pinned: 'left',
    colSpan: params => (params.node?.rowPinned ? 2 : 1),
    valueFormatter: params => (params.node?.rowPinned ? t('hourlyFeeGrid.total') : params.value || 0)
  },
  {
    headerName: t('hourlyFeeGrid.columns.name'),
    field: 'expertName',
    minWidth: 210,
    width: 230,
    pinned: 'left',
    tooltipField: 'expertName',
    wrapText: true,
    autoHeight: true,
    cellClass: 'hourly-fee-name-cell',
    cellStyle: {whiteSpace: 'normal', lineHeight: '1.2'},
    valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
  },
  {
    headerName: t('hourlyFeeGrid.columns.technician'),
    marryChildren: true,
    children: [
      // 单价（元/工日）→ 整数，非必填
      editableNumberCol2('unitPrice', t('hourlyFeeGrid.columns.unitPrice'), {decimals: 0}),
      // 工日数量（工日）→ 1位小数，非必填
      editableNumberCol3('workdayCount', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      // 费用小计（元）→ 计算列，整数
      calculatedFeeSubtotalCol('feeSubtotal', '费用小计（元）', 'unitPrice', 'workdayCount')
    ]
  },
  {
    headerName: t('hourlyFeeGrid.columns.assistantEngineer'),
    marryChildren: true,
    children: [
      // 单价（元/工日）→ 整数，非必填
      editableNumberCol2('unitPrice2', t('hourlyFeeGrid.columns.unitPrice'), {decimals: 0}),
      // 工日数量（工日）→ 1位小数，非必填
      editableNumberCol3('workdayCount2', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      // 费用小计（元）→ 计算列，整数
      calculatedFeeSubtotalCol('feeSubtotal2', '费用小计（元）', 'unitPrice2', 'workdayCount2')
    ]
  },
  {
    headerName: t('hourlyFeeGrid.columns.midEngineer'),
    marryChildren: true,
    children: [
      // 单价（元/工日）→ 整数，非必填
      editableNumberCol2('unitPrice3', t('hourlyFeeGrid.columns.unitPrice'), {decimals: 0}),
      // 工日数量（工日）→ 1位小数，非必填
      editableNumberCol3('workdayCount3', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      // 费用小计（元）→ 计算列，整数
      calculatedFeeSubtotalCol('feeSubtotal3', '费用小计（元）', 'unitPrice3', 'workdayCount3')
    ]
  },
  {
    headerName: t('hourlyFeeGrid.columns.seniorEngineer'),
    marryChildren: true,
    children: [
      // 单价（元/工日）→ 整数，非必填
      editableNumberCol2('unitPrice4', t('hourlyFeeGrid.columns.unitPrice'), {decimals: 0}),
      // 工日数量（工日）→ 1位小数，非必填
      editableNumberCol3('workdayCount4', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      // 费用小计（元）→ 计算列，整数
      calculatedFeeSubtotalCol('feeSubtotal4', '费用小计（元）', 'unitPrice4', 'workdayCount4')
    ]
  },
  {
    headerName: t('hourlyFeeGrid.columns.profSeniorEngineer'),
    marryChildren: true,
    children: [
      // 单价（元/工日）→ 整数，非必填
      editableNumberCol2('unitPrice5', t('hourlyFeeGrid.columns.unitPrice'), {decimals: 0}),
      // 工日数量（工日）→ 1位小数，非必填
      editableNumberCol3('workdayCount5', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      // 费用小计（元）→ 计算列，整数
      calculatedFeeSubtotalCol('feeSubtotal5', '费用小计（元）', 'unitPrice5', 'workdayCount5')
    ]
  },
  {
    headerName: t('hourlyFeeGrid.columns.total'),
    marryChildren: true,
    children: [
      /*editableNumberCol2('workdayCount6', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      editableNumberCol2('subtotal6', t('hourlyFeeGrid.columns.subtotal'), {decimals: 0}),*/
      calculatedWorkdayTotalCol('workdayCount6', t('hourlyFeeGrid.columns.workdayCount'), {decimals: 1}),
      calculatedSubTotalCol('feeSubtotal6', t('hourlyFeeGrid.columns.subtotal'), {decimals: 0}),
      calculatedAvgUnitPriceCol('avgUnitPrice', t('hourlyFeeGrid.columns.avgUnitPrice'), {decimals: 0})
    ]
  },
  /*editableMoneyCol('adoptedBudgetUnitPrice', t('hourlyFeeGrid.columns.adoptedBudgetUnitPrice')),
  editableNumberCol('personnelCount', t('hourlyFeeGrid.columns.personnelCount'), {
    aggFunc: decimalAggSum,
    valueParser: params => parseNonNegativeIntegerOrNull(params.newValue),
    valueFormatter: formatEditableInteger
  }),
  editableNumberCol('workdayCount', t('hourlyFeeGrid.columns.workdayCount'), {aggFunc: decimalAggSum}),
  {
    headerName: t('hourlyFeeGrid.columns.serviceBudget'),
    field: 'serviceBudget',
    headerClass: 'ag-right-aligned-header',
    minWidth: 120,
    flex: 1,
    editable: false,
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    aggFunc: decimalAggSum,
    valueGetter: params => (params.node?.rowPinned ? params.data?.serviceBudget ?? null : calcServiceBudget(params.data)),
    valueFormatter: params => {
      if (params.value == null || params.value === '') return ''
      return formatThousandsFlexible(params.value, 3)
    }
  },*/
  {
    headerName: t('hourlyFeeGrid.columns.remark'),
    field: 'remark',
    minWidth: 120,
    flex: 1,
    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 t('hourlyFeeGrid.clickToInput')
      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 && (params.value == null || params.value === '')
    }
  }
]
const gridColumnDefs = computed(() => withReadonlyAutoHeight(columnDefs))

const totalWorkdayCount = computed(() => sumByNumber(detailRows.value, row => row.workdayCount))
const totalWorkdayCount2 = computed(() => sumByNumber(detailRows.value, row => row.workdayCount2))
const totalWorkdayCount3 = computed(() => sumByNumber(detailRows.value, row => row.workdayCount3))
const totalWorkdayCount4 = computed(() => sumByNumber(detailRows.value, row => row.workdayCount4))
const totalWorkdayCount5 = computed(() => sumByNumber(detailRows.value, row => row.workdayCount5))
const totalFeeSubtotal = computed(() => {
  return sumByNumber(detailRows.value, row => row.feeSubtotal)
})
const totalFeeSubtotal2 = computed(() => sumByNumber(detailRows.value, row => row.feeSubtotal2))
const totalFeeSubtotal3 = computed(() => sumByNumber(detailRows.value, row => row.feeSubtotal3))
const totalFeeSubtotal4 = computed(() => sumByNumber(detailRows.value, row => row.feeSubtotal4))
const totalFeeSubtotal5 = computed(() => sumByNumber(detailRows.value, row => row.feeSubtotal5))
const totalFeeSubtotal6 = computed(() => {
  const allRows = fallbackDetailRows.value;
  return allRows.reduce((sum, row) => {
    const value = Number(row?.feeSubtotal6) || 0
    return sum + value
  }, 0);
})
const totalAvgUnitPrice = computed(() => sumByNumber(detailRows.value, row => row.avgUnitPrice))
const totalPersonnelCount = computed(() => sumByNumber(detailRows.value, row => row.personnelCount))
const totalServiceBudget = computed(() => sumNullableNumbers(detailRows.value.map(row => calcServiceBudget(row))))
const pinnedTopRowData = computed(() => {
  const result = [
    {
      id: 'pinned-total-row',
      expertCode: t('hourlyFeeGrid.total'),
      expertName: '',
      laborBudgetUnitPrice: '',
      compositeBudgetUnitPrice: '',
      adoptedBudgetUnitPrice: null,
      // personnelCount: totalPersonnelCount.value,
      unitPrice: '/',
      workdayCount: totalWorkdayCount.value,
      workdayCount2: totalWorkdayCount2.value,
      workdayCount3: totalWorkdayCount3.value,
      workdayCount4: totalWorkdayCount4.value,
      workdayCount5: totalWorkdayCount5.value,
      avgUnitPrice: totalAvgUnitPrice.value,
      feeSubtotal2: totalFeeSubtotal2.value,
      feeSubtotal3: totalFeeSubtotal3.value,
      feeSubtotal4: totalFeeSubtotal4.value,
      feeSubtotal5: totalFeeSubtotal5.value,
      feeSubtotal6: totalFeeSubtotal6.value,
      serviceBudget: totalServiceBudget.value,
      remark: '',
      path: ['TOTAL']
    }
  ]

  return result
})

/**
 * 保存数据到 IndexedDB
 * 保存时机：
 * 1. 单元格值变化时（handleCellValueChanged）
 * 2. 组件失活时（onDeactivated）- 切换页面
 * 3. 组件卸载前（onBeforeUnmount）- 关闭页面
 */

const recalculateServiceFees = () => {
  for (const row of fallbackDetailRows.value) {
    row.workdayCount6 = row.workdayCount + row.workdayCount2 + row.workdayCount3 + row.workdayCount4 + row.workdayCount5
    row.feeSubtotal6 = row.feeSubtotal + row.feeSubtotal2 + row.feeSubtotal3 + row.feeSubtotal4 + row.feeSubtotal5
    row.avgUnitPrice = row.feeSubtotal6 * row.workdayCount6
  }
}


const saveToIndexedDB = async () => {
  try {
    // syncServiceBudgetToRows()
    syncServiceBudgetToRows();
    // ✅ 使用 upsertBatch：存在则更新，不存在则新增
    const rows = detailRows.value.map(row => ({ ...row }))
    const stats = await useDataStore().upsertBatch(rows)
    
    console.log('💾 数据保存成功:', stats)
  } catch (error) {
    console.error('❌ saveToIndexedDB 失败:', error)
  }
}

/**
 * 从 IndexedDB 加载数据
 * 加载时机：
 * 1. 组件挂载时（onMounted）
 * 2. 组件激活时（onActivated）- 切换回页面
 * 3. storageKey 变化时（watch）
 */
const loadFromIndexedDB = async () => {
  try {

    // ✅ 查询 type 等于 'hourly' 的数据
    const hourlyRows = await useDataStore().query([
      { field: 'type', value: `${props.contractId}-hourly`, operator: 'eq' }
    ])
    // ✅ 将 DataItem[] 转换为 DetailRow[]
    const detailRowsFromStore: DetailRow[] = hourlyRows.map(row => ({
      id: String(row.id || ''),
      expertCode: String(row.expertCode || ''),
      expertName: String(row.expertName || ''),
      laborBudgetUnitPrice: String(row.laborBudgetUnitPrice || ''),
      compositeBudgetUnitPrice: String(row.compositeBudgetUnitPrice || ''),
      adoptedBudgetUnitPrice: row.adoptedBudgetUnitPrice != null ? Number(row.adoptedBudgetUnitPrice) : null,
      personnelCount: row.personnelCount != null ? Number(row.personnelCount) : null,
      workdayCount: row.workdayCount != null ? Number(row.workdayCount) : null,
      serviceBudget: row.serviceBudget != null ? Number(row.serviceBudget) : null,
      remark: String(row.remark || ''),
      path: Array.isArray(row.path) ? row.path : [],
      unitPrice: row.unitPrice != null ? Number(row.unitPrice) : null,
      workdayCount1: row.workdayCount1 != null ? Number(row.workdayCount1) : null,
      feeSubtotal: row.feeSubtotal != null ? Number(row.feeSubtotal) : null,
      unitPrice2: row.unitPrice2 != null ? Number(row.unitPrice2) : null,
      workdayCount2: row.workdayCount2 != null ? Number(row.workdayCount2) : null,
      feeSubtotal2: row.feeSubtotal2 != null ? Number(row.feeSubtotal2) : null,
      unitPrice3: row.unitPrice3 != null ? Number(row.unitPrice3) : null,
      workdayCount3: row.workdayCount3 != null ? Number(row.workdayCount3) : null,
      feeSubtotal3: row.feeSubtotal3 != null ? Number(row.feeSubtotal3) : null,
      unitPrice4: row.unitPrice4 != null ? Number(row.unitPrice4) : null,
      workdayCount4: row.workdayCount4 != null ? Number(row.workdayCount4) : null,
      feeSubtotal4: row.feeSubtotal4 != null ? Number(row.feeSubtotal4) : null,
      unitPrice5: row.unitPrice5 != null ? Number(row.unitPrice5) : null,
      workdayCount5: row.workdayCount5 != null ? Number(row.workdayCount5) : null,
      feeSubtotal5: row.feeSubtotal5 != null ? Number(row.feeSubtotal5) : null,
      workdayCount6: row.workdayCount6 != null ? Number(row.workdayCount6) : null,
      feeSubtotal6: row.feeSubtotal6 != null ? Number(row.feeSubtotal6) : null,
      avgUnitPrice: row.avgUnitPrice != null ? Number(row.avgUnitPrice) : null
    }))
    fallbackDetailRows.value = await mergeWithDictRows(detailRowsFromStore)
    console.log('✅ 转换后的 DetailRow 数据:', detailRowsFromStore)

  } catch (error) {
    detailRows.value = await buildDefaultRows()
  }
}

const relabelRowsFromExpertDict = async () => {
  if (detailRows.value.length === 0) return
  let changed = false
  detailRows.value = detailRows.value.map(row => {
    const match = String(row.id || '').match(/^expert-(\d+)$/)
    if (!match) return row
    const expert = (expertList as Record<string, ExpertLite | undefined>)[match[1]]
    if (!expert) return row
    const nextName = getExpertDisplayName(expert)
    if (row.expertName === nextName) return row
    changed = true
    return {
      ...row,
      expertName: nextName
    }
  })
  gridApi.value?.refreshCells({force: true})
  if (!changed) return
  await saveToIndexedDB()
}

let isBulkClipboardMutation = false

/**
 * 提交网格变更：同步计算值、刷新UI、保存到数据库
 * @param source 变更来源（用于日志追踪）
 */
const commitGridChanges = (source: string) => {
  console.log('🔄 提交网格变更:', source)
  syncServiceBudgetToRows()
  gridApi.value?.refreshCells({force: true})
  scheduleAutoRowHeights()
  void saveToIndexedDB()
}

/**
 * 处理单元格值变化事件
 * 触发时机：用户编辑完单元格后
 */
const handleCellValueChanged = (event?: any) => {
  /*if (isBulkClipboardMutation) {
    return
  }

  commitGridChanges('cell-value-changed')*/
  saveToIndexedDB()
}

const handleBulkMutationStart = () => {
  isBulkClipboardMutation = true
}

const handleBulkMutationEnd = (event?: any) => {
  // isBulkClipboardMutation = false
  // commitGridChanges(event?.type || 'bulk-end')
}

const handleGridReady = (event: GridReadyEvent<DetailRow>) => {
  gridApi.value = event.api
  scheduleAutoRowHeights()
}

let autoHeightSyncTimer: ReturnType<typeof setTimeout> | null = null
const isGridApiAlive = (api: GridApi<DetailRow> | null | undefined): api is GridApi<DetailRow> =>
    Boolean(api && !api.isDestroyed?.())

const forceRefreshCellsOnLiveApi = () => {
  // 再次触发一轮强制刷新，覆盖 AG Grid 异步布局后的高度计算。
  setTimeout(() => {
    const liveApi = gridApi.value
    if (!isGridApiAlive(liveApi)) return
    liveApi.refreshCells({force: true})
    liveApi.redrawRows()
  }, 16)
}

const syncAutoRowHeights = async () => {
  await nextTick()
  const api = gridApi.value
  if (!isGridApiAlive(api)) return
  api.onRowHeightChanged()
  api.refreshCells({force: true})
  api.redrawRows()
  forceRefreshCellsOnLiveApi()
}
const scheduleAutoRowHeights = () => {
  if (autoHeightSyncTimer) clearTimeout(autoHeightSyncTimer)
  autoHeightSyncTimer = setTimeout(() => {
    autoHeightSyncTimer = null
    if (!isGridApiAlive(gridApi.value)) return
    void syncAutoRowHeights()
  }, 0)
}

const onGridSizeChanged = () => {
  scheduleAutoRowHeights()
}

const onColumnResized = () => {
  scheduleAutoRowHeights()
}

const onFirstDataRendered = (_event: FirstDataRenderedEvent<DetailRow>) => {
  scheduleAutoRowHeights()
}

const onRowDataUpdated = (_event: RowDataUpdatedEvent<DetailRow>) => {
  scheduleAutoRowHeights()
}

const processCellForClipboard = (params: any) => {
  if (Array.isArray(params.value)) return JSON.stringify(params.value)
  return params.value
}

const processCellFromClipboard = (params: any) => {
  const field = String(params.column?.getColDef?.().field || '')
  if (field === 'personnelCount') {
    return parseNonNegativeIntegerOrNull(params.value)
  }
  if (field === 'adoptedBudgetUnitPrice' || field === 'workdayCount') {
    return parseNumberOrNull(params.value, {precision: 3})
  }
  try {
    const parsed = JSON.parse(params.value)
    if (Array.isArray(parsed)) return parsed
  } catch (_error) {
    return params.value
  }
  return params.value
}

/**
 * 组件挂载时加载数据
 */
onMounted(async () => {
  console.log('🚀 组件挂载')
  await loadFromIndexedDB()
  scheduleAutoRowHeights()
})

/**
 * 组件激活时重新加载数据（从其他页面切换回来时）
 */
onActivated(async () => {
  console.log('🔙 组件激活')
  await loadFromIndexedDB()
  scheduleAutoRowHeights()
})

watch(
    () => useDataStore().items,
    async (newItems) => {
      if (Object.keys(newItems).length > 0) {
        loadFromIndexedDB();
      }
    },
    { immediate: true, deep: true }
)

watch(
    () => props.storageKey,
    () => {
      void loadFromIndexedDB()
      scheduleAutoRowHeights()
    }
)

watch(
    () => detailRows.value.length,
    () => {
      scheduleAutoRowHeights()
    }
)

watch(
    () => locale.value,
    () => {
      void relabelRowsFromExpertDict()
    }
)

/**
 * 组件失活时保存数据（切换页面时触发）
 * 配合 Vue 的 <keep-alive> 使用
 */
onDeactivated(() => {
  console.log('🔄 组件失活，保存数据')
  gridApi.value?.stopEditing()
  void saveToIndexedDB()
})

/**
 * 组件卸载前保存数据（关闭页面时触发）
 */
onBeforeUnmount(() => {
  console.log('💔 组件卸载，保存数据')
  gridApi.value?.stopEditing()
  gridApi.value = null
  if (autoHeightSyncTimer) {
    clearTimeout(autoHeightSyncTimer)
    autoHeightSyncTimer = null
  }
  void saveToIndexedDB()
})
</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">{{ props.title || t('hourlyFeeGrid.title') }}</h3>
        <div class="text-xs text-muted-foreground"></div>
      </div>

      <div :class="agGridWrapClass">
        <AgGridVue
            :style="agGridStyle"
            :rowData="detailRows"
            :pinnedBottomRowData="pinnedTopRowData"
            :columnDefs="gridColumnDefs"
            :gridOptions="gridOptions"
            :theme="myTheme"
            :animateRows="true"
            :treeData="false"
            @cell-value-changed="handleCellValueChanged"
            @paste-start="handleBulkMutationStart"
            @paste-end="handleBulkMutationEnd"
            @fill-start="handleBulkMutationStart"
            @fill-end="handleBulkMutationEnd"
            :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"
            @grid-ready="handleGridReady"
            @first-data-rendered="onFirstDataRendered"
            @row-data-updated="onRowDataUpdated"
            @grid-size-changed="onGridSizeChanged"
            @column-resized="onColumnResized"
        />
      </div>
    </div>
  </div>
</template>

<style scoped>
:deep(.hourly-fee-name-cell.ag-cell-auto-height) {
  display: flex;
  align-items: center;
}

:deep(.hourly-fee-name-cell.ag-cell-auto-height .ag-cell-wrapper),
:deep(.hourly-fee-name-cell.ag-cell-auto-height .ag-cell-value) {
  display: flex;
  align-items: center;
  width: 100%;
  white-space: normal;
}
</style>
