<script setup lang="ts">
import { computed, 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 { expertList } from '@/sql'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { decimalAggSum, roundTo, sumByNumber, toDecimal } from '@/lib/decimal'
import { formatThousands } from '@/lib/numberFormat'
import { syncPricingTotalToZxFw, ZXFW_RELOAD_SERVICE_KEY } from '@/lib/zxFwPricingSync'
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'

import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale';

interface DetailRow {
  id: string
  expertCode: string
  expertName: string
  laborBudgetUnitPrice: string
  compositeBudgetUnitPrice: string
  adoptedBudgetUnitPrice: number | null
  personnelCount: number | null
  workdayCount: number | null
  serviceBudget: number | null
  remark: string
  path: string[]
}

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

const props = defineProps<{
  contractId: string,

  serviceId: string | number
}>()
const DB_KEY = computed(() => `hourlyPricing-${props.contractId}-${props.serviceId}`)
const PRICING_CLEAR_SKIP_PREFIX = 'pricing-clear-skip:'
const PRICING_FORCE_DEFAULT_PREFIX = 'pricing-force-default:'
const pricingPaneReloadStore = usePricingPaneReloadStore()

const shouldSkipPersist = () => {
  const storageKey = `${PRICING_CLEAR_SKIP_PREFIX}${DB_KEY.value}`
  const raw = sessionStorage.getItem(storageKey)
  if (!raw) return false
  const skipUntil = Number(raw)
  if (Number.isFinite(skipUntil) && Date.now() <= skipUntil) return true
  sessionStorage.removeItem(storageKey)
  return false
}

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 detailRows = ref<DetailRow[]>([])

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

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 = (): DetailRow[] => {
  const rows: DetailRow[] = []
  for (const [expertId, expert] of expertEntries) {
    const rowId = `expert-${expertId}`
    rows.push({
      id: rowId,
      expertCode: expert.code,
      expertName: expert.name,
      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 = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => {
  const dbValueMap = new Map<string, DetailRow>()
  for (const row of rowsFromDb || []) {
    dbValueMap.set(row.id, row)
  }

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

    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 : ''
    }
  })
}

const parseNumberOrNull = (value: unknown) => {
  if (value === '' || value == null) return null
  const v = Number(value)
  return Number.isFinite(v) ? v : null
}

const parseNonNegativeIntegerOrNull = (value: unknown) => {
  if (value === '' || value == null) return null
  if (typeof value === 'number') {
    return Number.isInteger(value) && value >= 0 ? value : null
  }
  const normalized = String(value).trim()
  if (!/^\d+$/.test(normalized)) return null
  const v = Number(normalized)
  return Number.isSafeInteger(v) ? v : null
}

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 formatEditableInteger = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return '点击输入'
  }
  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 editableNumberCol = <K extends keyof DetailRow>(
  field: K,
  headerName: string,
  extra: Partial<ColDef<DetailRow>> = {}
): ColDef<DetailRow> => ({
  headerName,
  field,
  minWidth: 150,
  flex: 1,
  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),
  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: 150,
  flex: 1,
  editable: params => !params.node?.group && !params.node?.rowPinned,
  cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'ag-right-aligned-cell editable-cell-line' : 'ag-right-aligned-cell'),
  cellClassRules: {
    'editable-cell-empty': params =>
      !params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')
  },
  valueParser: params => parseNumberOrNull(params.newValue),
  valueFormatter: params => {
    if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
      return '点击输入'
    }
    if (params.value == null) return ''
    return formatThousands(params.value)
  },
  ...extra
})

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

const columnDefs: (ColDef<DetailRow> | ColGroupDef<DetailRow>)[] = [
  {
    headerName: '编码',
    field: 'expertCode',
    minWidth: 120,
    width: 140,
    pinned: 'left',
    colSpan: params => (params.node?.rowPinned ? 2 : 1),
    valueFormatter: params => (params.node?.rowPinned ? '总合计' : params.value || '')
  },
  {
    headerName: '人员名称',
    field: 'expertName',
    minWidth: 200,
    width: 220,
    pinned: 'left',
    tooltipField: 'expertName',
    valueFormatter: params => (params.node?.rowPinned ? '' : params.value || '')
  },
  {
    headerName: '预算参考单价',
    marryChildren: true,
    children: [
      readonlyTextCol('laborBudgetUnitPrice', '人工预算单价(元/工日)'),
      readonlyTextCol('compositeBudgetUnitPrice', '综合预算单价(元/工日)')
    ]
  },
  editableMoneyCol('adoptedBudgetUnitPrice', '预算采用单价(元/工日)'),
  editableNumberCol('personnelCount', '人员数量(人)', {
    aggFunc: decimalAggSum,
    valueParser: params => parseNonNegativeIntegerOrNull(params.newValue),
    valueFormatter: formatEditableInteger
  }),
  editableNumberCol('workdayCount', '工日数量(工日)', { aggFunc: decimalAggSum }),
  {
    headerName: '服务预算（元)',
    field: 'serviceBudget',
    headerClass: 'ag-right-aligned-header',
    minWidth: 150,
    flex: 1,
    cellClass: 'ag-right-aligned-cell',
    editable: false,
    aggFunc: decimalAggSum,
    valueGetter: params => (params.node?.rowPinned ? params.data?.serviceBudget ?? null : calcServiceBudget(params.data)),
    valueFormatter: params => {
      if (params.value == null || params.value === '') return ''
      return formatThousands(params.value)
    }
  },
  {
    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 totalPersonnelCount = computed(() => sumByNumber(detailRows.value, row => row.personnelCount))

const totalWorkdayCount = computed(() => sumByNumber(detailRows.value, row => row.workdayCount))

const totalServiceBudget = computed(() => sumByNumber(detailRows.value, row => calcServiceBudget(row)))
const pinnedTopRowData = computed(() => [
  {
    id: 'pinned-total-row',
    expertCode: '总合计',
    expertName: '',
    laborBudgetUnitPrice: '',
    compositeBudgetUnitPrice: '',
    adoptedBudgetUnitPrice: null,
    personnelCount: totalPersonnelCount.value,
    workdayCount: totalWorkdayCount.value,
    serviceBudget: totalServiceBudget.value,
    remark: '',
    path: ['TOTAL']
  }
])



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

const loadFromIndexedDB = async () => {
  try {
    if (shouldForceDefaultLoad()) {
      detailRows.value = buildDefaultRows()
      return
    }

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

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

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()
})

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;
};

const handleGridReady = (params: any) => {
  const w = window as any
  if (!w.__agGridApis) w.__agGridApis = {}
  w.__agGridApis = params.api
}
</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>
        <div class="text-xs text-muted-foreground">导入导出</div>
      </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" :gridOptions="gridOptions" :theme="myTheme" :treeData="false"
          @grid-ready="handleGridReady"
          @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>
