<script setup lang="ts">
import {h, computed, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch, defineComponent} 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 { Pencil, Eraser, Trash2 } from 'lucide-vue-next'
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, GridOptions} from 'ag-grid-community';
import {useKvStore} from "@/pinia/kv";
import {useDataStore} from '@/pinia/zx'
/**
 * 数据行接口定义
 * 注意：此接口需与 columnDefs 中的字段保持一致
 */
interface DetailRow {
  id: string
  num: number | string | null
  expertCode: string
  expertName: string
  workdayCount: number | string  | null
  serviceBudget: number | string  | null
  remark: string
  path: string[],
  feeItem: string,
  unit: string,
  quantity: number | string | null,
  unitPrice: number | string | null,
  serviceFee: number | null | string,
  remark: string,
  actions: null
  // 新增：按职称分类的工时字段（与 columnDefs 对应）
}

// 接口定义（确认有这些字段）
interface WorkRow {
  id: string
  // ... 其他现有字段
  feeItem: string
  unit: string
  quantity: number | null
  unitPrice: number | null
  serviceFee: number | null
  remark: string
}

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 clearConfirmOpen = ref(false)
const deleteConfirmOpen = ref(false)
// 添加这些变量到组件顶部
const pendingClearRowId = ref<string | null>(null)
const pendingDeleteRowId = ref<string | null>(null)
const pendingClearRowName = computed(() => {
  if (!pendingClearRowId.value) return ''
  const row = detailRows.value.find(item => item.id === pendingClearRowId.value)
  return row ? `${row.feeItem}` : pendingClearRowId.value
})
const pendingDeleteRowName = computed(() => {
  if (!pendingDeleteRowId.value) return ''
  const row = detailRows.value.find(item => item.id === pendingDeleteRowId.value)
  return row ? `${row.feeItem}` : pendingDeleteRowId.value
})

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 contractId = props.contractId
  const rowId = `${contractId}-other-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
  rows.push({
    id: rowId,
    type: `${props.contractId}-other`,
    feeItem: 0,
    unit: '',
    quantity: 0,
    unitPrice: 0,
    serviceFee: 0,
    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)
  }*/
  let results = []
  if (rowsFromDb.length > 0) {
    for (const row of rowsFromDb || []) {
      results.push({...row})
    }
    return results;
  }
  const defaultRows = await buildDefaultRows()
  return defaultRows;
  /*return defaultRows.map(row => {
    /!*const fromDb = dbValueMap.get(row.id)
    if (!fromDb) return row
*!/
    // 使用展开运算符合并所有字段，确保新增字段也能正确合并
    return {
      ...row,
      // 保留数据库中的所有可编辑字段
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : '',
      feeItem: fromDb.feeItem,
      unit: fromDb.unit,
      unitPrice: fromDb.unitPrice,
      serviceFee: fromDb.serviceFee,
      quantity: fromDb.quantity,
      // 合并新增的职称分类字段
    }
  })*/
}

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)

    // 计算平均单价
    const unitPrice = row.unitPrice ?? 0
    const quantity = row.quantity ?? 0
    row.serviceFee = unitPrice * quantity
  }
}

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',
    valueFormatter: (params: ValueFormatterParams) => {
      return Math.round(params.data.avgUnitPrice)
    }
  }
}

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 ActionCellRenderer = defineComponent({
  name: 'ActionCellRenderer',
  props: {
    params: {
      type: Object as PropType<ICellRendererParams<DetailRow>>,
      required: true
    }
  },
  setup(props) {
    return () => {
      if (props.params.node?.rowPinned === 'bottom') {
        return null
      }

      return h('div', { class: 'zxfw-action-wrap' }, [
        h('div', { class: 'zxfw-action-group' }, [
          // h('button', { class: 'zxfw-action-btn', 'data-action': 'edit', type: 'button' }, [
          //   h(Pencil, { size: 13, 'aria-hidden': 'true' }),
          //   h('span', t('htZxFw.edit'))
          // ]),
          h('button', { class: 'zxfw-action-btn', 'data-action': 'clear', type: 'button' }, [
            h(Eraser, { size: 13, 'aria-hidden': 'true' }),
            h('span', t('htZxFw.resetDefault'))
          ]),
          h('button', { class: 'zxfw-action-btn zxfw-action-btn--danger', 'data-action': 'delete', type: 'button' }, [
            h(Trash2, { size: 13, 'aria-hidden': 'true' }),
            h('span', t('htZxFw.delete'))
          ])
        ])
      ])
    }
  }
})

const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
  {
    headerName: t('otherService.columns.num'),
    minWidth: 90,
    width: 100,
    editable: false,
    colSpan: params => (params.node?.rowPinned ? 2 : 1),
    valueGetter: params => {
      // 1. 如果是底部汇总行（Pinned Bottom），显示 "/" 或 "合计"
      if (params.node?.rowPinned === 'bottom') {
        return '/';
      }
      // 2. 否则返回当前行索引 + 1（从 1 开始计数）
      return String(params.node.rowIndex + 1);
    },

    cellClass: ['text-center', 'font-mono'], // 数字右对齐或居中，使用等宽字体更整齐
    filter: false,                // 序号列通常不需要参与过滤
    valueFormatter: params => (params.node?.rowPinned ? t('hourlyFeeGrid.total') : params.value || 0)
  },
  {
    headerName: t('otherService.columns.feeItem'),
    field: 'feeItem',
    flex: 2,
    minWidth: 200,
    wrapText: true,
    autoHeight: true,
    editable: true,
    cellClass: 'ag-left-aligned-cell',
    valueFormatter: params => params.value || '',
    placeholder: '请填写费用项名称'
  },
  {
    headerName: t('otherService.columns.unit'),
    field: 'unit',
    width: 150,
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    valueFormatter: params => params.value || '',
    placeholder: '填写计量单位'
  },
  {
    headerName: t('otherService.columns.unitPrice'),
    field: 'unitPrice',
    width: 150,
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    cellDataType: 'number',
    valueFormatter: params => params.value || '',
    placeholder: '填写计量单位'
  },
  {
    headerName: t('otherService.columns.quantity'),
    field: 'quantity',
    width: 150,
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    cellDataType: 'number',
    valueParser: (params: ValueParserParams) => parseNumberOrNull(params.newValue, { precision: 2 }),
    /*valueFormatter: (params: ValueFormatterParams) => {
      const val = Number(params.value ?? 0)
      return val.toFixed(2)  // 👈 2位小数显示
    },*/
    cellClassRules: {
      'editable-cell-empty': params =>
          !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
    }
  },
  {
    headerName: t('otherService.columns.serviceFee'),
    field: 'serviceFee',
    minWidth: 210,
    width: 230,
    tooltipField: 'serviceFee',
    wrapText: true,
    autoHeight: true,
    cellClass: 'hourly-fee-name-cell',
    cellStyle: {whiteSpace: 'normal', lineHeight: '1.2'},
    /*valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')*/
  },
  {
    headerName: t('otherService.columns.remark'),
    field: 'remark',
    minWidth: 120,
    flex: 1,
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: {whiteSpace: 'normal', lineHeight: '1.4'},
    editable: (params) => {
      if (params.node?.rowPinned === 'bottom') {
        return false;
      } else {
        return true;
      }
    },
    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 === '')
    }
  },
  {
    headerName: t('otherService.columns.actions'),
    field: 'actions',
    minWidth: 210,
    width: 230,
    tooltipField: 'actions',
    wrapText: true,
    autoHeight: true,
    cellClass: 'hourly-fee-name-cell',
    cellStyle: {whiteSpace: 'normal', lineHeight: '1.2'},
    cellRenderer: ActionCellRenderer,
    valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
  }
]

// 判断是否为固定行（如汇总行）
const isFixedRow = (row: DetailRow): boolean => {
  return row.id === 'pinned-total-row' || row.path?.includes('TOTAL')
}

// 清空单行数据（恢复默认值）
const clearRowValues = async (row: DetailRow) => {
  // 保留id和path，其他字段重置为默认值
  const defaultRow = await buildDefaultRows()
  const newRow = {
    ...defaultRow[0],
    id: row.id,
    path: row.path,
    type: row.type
  }

  // 替换原行
  const index = detailRows.value.findIndex(r => r.id === row.id)
  if (index !== -1) {
    detailRows.value[index] = newRow
    await nextTick()
    gridApi.value?.refreshCells({ force: true })
    await saveToIndexedDB()
  }
}

// 删除单行
const removeRow = async (row: DetailRow) => {
  const index = detailRows.value.findIndex(r => r.id === row.id)
  if (index !== -1) {
    detailRows.value.splice(index, 1)
    await nextTick()
    gridApi.value?.refreshCells({ force: true })
    try {
      await useDataStore().deleteItem(row.id)
      console.log(`🗑️ 已从数据库删除: ${row.id}`)
    } catch (error) {
      console.error('❌ 从数据库删除失败:', error)
    }
  }
}

/**
 * 计算待清空服务的显示名称
 * 优先从当前明细行获取，其次从服务词典获取
 */
const pendingClearServiceName = computed(() => {
  /*if (!pendingClearServiceId.value) return ''
  const row = detailRows.value.find(item => item.id === pendingClearServiceId.value)
  if (row) return `${row.code}${row.name}`
  const dict = serviceById.value.get(pendingClearServiceId.value)
  if (dict) return `${dict.code}${dict.name}`
  return pendingClearServiceId.value*/
})

/**
 * 计算待删除服务的显示名称
 * 优先从当前明细行获取，其次从服务词典获取
 */
const pendingDeleteServiceName = computed(() => {
  /*if (!pendingDeleteServiceId.value) return ''
  const row = detailRows.value.find(item => item.id === pendingDeleteServiceId.value)
  if (row) return `${row.code}${row.name}`
  const dict = serviceById.value.get(pendingDeleteServiceId.value)
  if (dict) return `${dict.code}${dict.name}`
  return pendingDeleteServiceId.value*/
})

/** 清空确认框开关回调。 */
const handleClearConfirmOpenChange = (open: boolean) => {
  clearConfirmOpen.value = open
}

/** 删除确认框开关回调。 */
const handleDeleteConfirmOpenChange = (open: boolean) => {
  deleteConfirmOpen.value = open
}

/** 触发“恢复默认”二次确认。 */
const requestClearRow = (row: DetailRow) => {
  pendingClearRowId.value = row.id
  clearConfirmOpen.value = true
}

const cancelClearRow = () => {
  clearConfirmOpen.value = false
  pendingClearRowId.value = null
}

/** 执行“恢复默认”并关闭确认框。 */
const confirmClearRow = async () => {
  if (!pendingClearRowId.value) return
  const row = detailRows.value.find(item => item.id === pendingClearRowId.value)
  if (row) {
    await clearRowValues(row)
  }
  clearConfirmOpen.value = false
  pendingClearRowId.value = null
}

/** 触发“删除服务”二次确认。 */
const requestDeleteRow = (row: DetailRow) => {
  pendingDeleteRowId.value = row.id
  deleteConfirmOpen.value = true
}

const cancelDeleteRow = () => {
  deleteConfirmOpen.value = false
  pendingDeleteRowId.value = null
}

/** 执行删除并关闭确认框。 */
const confirmDeleteRow = async () => {
  if (!pendingDeleteRowId.value) return
  const row = detailRows.value.find(item => item.id === pendingDeleteRowId.value)
  if (row) {
    await removeRow(row)
  }
  deleteConfirmOpen.value = false
  pendingDeleteRowId.value = null
}
const detailGridOptions: GridOptions<DetailRow> = {
  ...gridOptions,
  treeData: false,
  getDataPath: undefined,
  context: {
    onCellClicked: async params => {
      // 只处理 actions 列的点击
      if (params.colDef.field !== 'actions' || !params.data) return

      // 检查是否点击了按钮
      const target = params.event?.target as HTMLElement | null
      const btn = target?.closest('button[data-action]') as HTMLButtonElement | null
      const action = btn?.dataset.action
      debugger
      if (action === 'clear') {
        requestClearRow(params.data)
        return
      }

      if (action === 'delete') {
        requestDeleteRow(params.data)
      }
    }
  }
}
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 0;
})
const totalAvgUnitPrice = computed(() => sumByNumber(detailRows.value, row => row.avgUnitPrice))
const totalPersonnelCount = computed(() => sumByNumber(detailRows.value, row => row.personnelCount))
const totalServiceBudget = computed(() => {
  const allRows = detailRows.value;
  return allRows.reduce((sum, row) => {
    const value = Number(row?.serviceFee) || 0
    return sum + value
  }, 0);
})
const pinnedTopRowData = computed(() => {
  const result = [
    {
      id: 'pinned-total-row',
      expertCode: t('otherService.total'),
      expertName: '',
      laborBudgetUnitPrice: '',
      compositeBudgetUnitPrice: '',
      adoptedBudgetUnitPrice: null,
      // personnelCount: totalPersonnelCount.value,
      unitPrice: '/',
      unit: '/',
      serviceFee: 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
  }
}

// 1. 添加单行
const addWorkRow = () => {
  const contractId = props.contractId
  const rowId = `${contractId}-other-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
  detailRows.value.push({
    id: rowId,
    type: `${props.contractId}-other`,
    // 填充所有必需字段的默认值
    feeItem: '',
    unit: '',
    quantity: null,
    unitPrice: null,
    serviceFee: null,
    remark: '',
    path: [rowId]
  })
  // 立即保存
  void saveToIndexedDB()
}

// 2. 批量重置（如果需要）
const resetGrid = () => {
  if(!confirm('确定重置吗？')) return;
  detailRows.value = []
  nextTick(() => gridApi.value?.refreshCells({ force: true }))
  void saveToIndexedDB()
}

const saveToIndexedDB = async () => {
  try {
    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}-other`, operator: 'eq' }
    ])
    // ✅ 将 DataItem[] 转换为 DetailRow[]
    const detailRowsFromStore: DetailRow[] = hourlyRows.map((row,idx) => ({
      num: idx + 1,
      id: String(row.id || ''),
      type: String(row.type || ''),
      expertCode: String(row.expertCode || ''),
      expertName: String(row.expertName || ''),
      remark: String(row.remark || ''),
      path: Array.isArray(row.path) ? row.path : [],
      feeItem: row.feeItem ?? 0,
      unit: row.unit,
      quantity: row.quantity ?? 0,
      unitPrice: row.unitPrice ?? 0,
      serviceFee: row.serviceFee ?? 0
    }))
    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
}

const handleCellClicked = (params: any) => {
  // 只处理 actions 列的点击
  if (params.colDef.field !== 'actions' || !params.data) return

  // 检查是否点击了按钮
  const target = params.event?.target as HTMLElement | null
  const btn = target?.closest('button[data-action]') as HTMLButtonElement | null
  const action = btn?.dataset.action

  if (action === 'clear') {
    requestClearRow(params.data)
    return
  }

  if (action === 'delete') {
    requestDeleteRow(params.data)
  }
}

/**
 * 组件挂载时加载数据
 */
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('otherService.title') }}</h3>
        <div class="text-xs text-muted-foreground"></div>
      </div>

      <div class="px-6 py-4 border-b bg-slate-50 flex items-center justify-between shrink-0">

        <h3 class="font-bold text-base">{{ title || "费用明细" }}</h3>
        <div class="flex gap-3">
          <!-- 新增按钮 -->
          <button
              @click="addWorkRow"
              class="inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-white transition-colors bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
          >
            <span class="mr-2 text-xl leading-none">+</span> 新增条目
          </button>

<!--          &lt;!&ndash; 清空按钮 &ndash;&gt;
          <button
              @click="resetGrid"
              class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
          >
            全部清空
          </button>-->
        </div>
      </div>

      <div :class="agGridWrapClass">
        <AgGridVue
            :gridOptions="detailGridOptions"
            :style="agGridStyle"
            :rowData="detailRows"
            :pinnedBottomRowData="pinnedTopRowData"
            :columnDefs="gridColumnDefs"
            :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"
            @cell-clicked="handleCellClicked"
            @grid-ready="handleGridReady"
            @first-data-rendered="onFirstDataRendered"
            @row-data-updated="onRowDataUpdated"
            @grid-size-changed="onGridSizeChanged"
            @column-resized="onColumnResized"
        />
      </div>
    </div>
  </div>

  <AlertDialogRoot v-if="clearConfirmOpen" :open="clearConfirmOpen" @update:open="handleClearConfirmOpenChange">
    <AlertDialogPortal>
      <AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
      <AlertDialogContent
          class="fixed left-1/2 top-1/2 z-[70] 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">{{ t('htZxFw.dialog.resetTitle') }}</AlertDialogTitle>
        <AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
          {{ t('htZxFw.dialog.resetDesc', { name: pendingClearServiceName }) }}
        </AlertDialogDescription>
        <div class="mt-4 flex items-center justify-end gap-2">
          <AlertDialogCancel as-child>
            <Button variant="outline" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400"  @click="cancelClearRow">{{ t('common.cancel') }}</Button>
          </AlertDialogCancel>
          <AlertDialogAction as-child>
            <Button variant="destructive" class="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-red-600 rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" @click="confirmClearRow">{{ t('htZxFw.dialog.confirmReset') }}</Button>
          </AlertDialogAction>
        </div>
      </AlertDialogContent>
    </AlertDialogPortal>
  </AlertDialogRoot>

  <AlertDialogRoot v-if="deleteConfirmOpen" :open="deleteConfirmOpen" @update:open="handleDeleteConfirmOpenChange">
    <AlertDialogPortal>
      <AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
      <AlertDialogContent
          class="fixed left-1/2 top-1/2 z-[70] 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">{{ t('htZxFw.dialog.deleteTitle') }}</AlertDialogTitle>
        <AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
          {{ t('htZxFw.dialog.deleteDesc', { name: pendingDeleteServiceName }) }}
        </AlertDialogDescription>
        <div class="mt-4 flex items-center justify-end gap-2">
          <AlertDialogCancel as-child>
            <Button variant="outline" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-400" @click="cancelDeleteRow">{{ t('common.cancel') }}</Button>
          </AlertDialogCancel>
          <AlertDialogAction as-child>
            <Button variant="destructive" class="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-red-600 rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" @click="confirmDeleteRow">{{ t('common.delete') }}</Button>
          </AlertDialogAction>
        </div>
      </AlertDialogContent>
    </AlertDialogPortal>
  </AlertDialogRoot>
</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>
