<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef } from 'ag-grid-community'
import localforage from 'localforage'
import { serviceList, taskList } from '@/sql'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { decimalAggSum } from '@/lib/decimal'
import { usePricingPaneReloadStore } from '@/pinia/pricingPaneReload'

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

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

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

const props = defineProps<{
  contractId: string,

  serviceId: string | number
}>()
const DB_KEY = computed(() => `gzlF-${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 serviceLite = { defCoe: number | null }
type taskLite = {
  serviceID: number
  code: string
  name: string
  basicParam: string
  unit: string
  conversion: number | null
  maxPrice: number | null
  minPrice: number | null
  defPrice: number | null
  desc: string | null
}

const defaultConsultCategoryFactor = computed<number | null>(() => {
  const service = (serviceList as Record<string, serviceLite | undefined>)[String(props.serviceId)]
  return typeof service?.defCoe === 'number' && Number.isFinite(service.defCoe) ? service.defCoe : null
})

const formatTaskReferenceUnitPrice = (task: taskLite) => {
  const unit = task.unit || ''
  const hasMin = typeof task.minPrice === 'number' && Number.isFinite(task.minPrice)
  const hasMax = typeof task.maxPrice === 'number' && Number.isFinite(task.maxPrice)
  if (hasMin && hasMax) return `${task.minPrice}${unit}-${task.maxPrice}${unit}`
  if (hasMin) return `${task.minPrice}${unit}`
  if (hasMax) return `${task.maxPrice}${unit}`
  return ''
}

const buildDefaultRows = (): DetailRow[] => {
  const rows: DetailRow[] = []
  const currentServiceId = Number(props.serviceId)
  const sourceTaskIds = Object.entries(taskList as Record<string, taskLite>)
    .filter(([, task]) => Number(task.serviceID) === currentServiceId)
    .map(([key]) => Number(key))
    .filter(Number.isFinite)
    .sort((a, b) => a - b)

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

  if (rows.length === 0) {
    const emptyRowId = `task-none-${String(props.serviceId)}`
    rows.push({
      id: emptyRowId,
      taskCode: '无',
      taskName: '无',
      unit: '',
      conversion: null,
      workload: null,
      budgetBase: '无',
      budgetReferenceUnitPrice: '无',
      budgetAdoptedUnitPrice: null,
      consultCategoryFactor: null,
      serviceFee: null,
      remark: '',
      path: [emptyRowId]
    })
  }

  return rows
}

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

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,
      workload: typeof fromDb.workload === 'number' ? fromDb.workload : null,
      budgetAdoptedUnitPrice:
        typeof fromDb.budgetAdoptedUnitPrice === 'number' ? fromDb.budgetAdoptedUnitPrice : null,
      consultCategoryFactor:
        typeof fromDb.consultCategoryFactor === 'number' ? fromDb.consultCategoryFactor : null,
      serviceFee: typeof fromDb.serviceFee === 'number' ? fromDb.serviceFee : null,
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : ''
    }
  })
}

const parseNumberOrNull = (value: unknown) => {
  if (value === '' || value == null) return null
  const normalized = typeof value === 'string' ? value.replace(/[^0-9.\-]/g, '') : value
  const v = Number(normalized)
  return Number.isFinite(v) ? v : null
}

const calcServiceFee = (row: DetailRow | undefined) => {
  if (!row || isNoTaskRow(row)) return null
  const price = row.budgetAdoptedUnitPrice
  const conversion = row.conversion
  const workload = row.workload
  const factor = row.consultCategoryFactor
  if (
    typeof price !== 'number' ||
    !Number.isFinite(price) ||
    typeof conversion !== 'number' ||
    !Number.isFinite(conversion) ||
    typeof workload !== 'number' ||
    !Number.isFinite(workload) ||
    typeof factor !== 'number' ||
    !Number.isFinite(factor)
  ) {
    return null
  }
  return price * conversion * workload * factor
}

const formatEditableNumber = (params: any) => {
  if (isNoTaskRow(params.data)) return '无'
  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 spanRowsByTaskName = (params: any) => {
  const rowA = params?.nodeA?.data as DetailRow | undefined
  const rowB = params?.nodeB?.data as DetailRow | undefined
  // debugger
  if (!rowA || !rowB) return false
  if (isNoTaskRow(rowA) || isNoTaskRow(rowB)) return false

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

const columnDefs: ColDef<DetailRow>[] = [
  {
    headerName: '编码',
    field: 'taskCode',
    minWidth: 100,
    width: 120,
    pinned: 'left',
    valueFormatter: params => params.value || ''
  },
  {
    headerName: '名称',
    field: 'taskName',
    minWidth: 150,
    width: 220,
    pinned: 'left',
        autoHeight: true,

    spanRows: true,
    valueFormatter: params => params.value || ''
  },
  {
    headerName: '预算基数',
    field: 'budgetBase',
    minWidth: 150,
        autoHeight: true,

    width: 180,
    pinned: 'left',
    spanRows: spanRowsByTaskName,
    valueFormatter: params => params.value || ''
  },
  {
    headerName: '预算参考单价',
    field: 'budgetReferenceUnitPrice',
    minWidth: 170,
    flex: 1,
    valueFormatter: params => params.value || ''
  },
  {
    headerName: '预算采用单价',
    field: 'budgetAdoptedUnitPrice',
    minWidth: 170,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    valueParser: params => parseNumberOrNull(params.newValue),
    valueFormatter: params => {
      if (isNoTaskRow(params.data)) return '无'
      if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
        return '点击输入'
      }
      if (params.value == null) return ''
      const unit = params.data?.unit || ''
      return `${Number(params.value).toFixed(2)}${unit}`
    }
  },
  {
    headerName: '工作量',
    field: 'workload',
    minWidth: 140,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    aggFunc: decimalAggSum,
    valueParser: params => parseNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber
  },
  {
    headerName: '咨询分类系数',
    field: 'consultCategoryFactor',
    minWidth: 150,
    flex: 1,
    editable: params => !params.node?.group && !params.node?.rowPinned && !isNoTaskRow(params.data),
    cellClass: params => (!params.node?.group && !params.node?.rowPinned ? 'editable-cell-line' : ''),
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group &&
        !params.node?.rowPinned &&
        !isNoTaskRow(params.data) &&
        (params.value == null || params.value === '')
    },
    valueParser: params => parseNumberOrNull(params.newValue),
    valueFormatter: formatEditableNumber
  },
  {
    headerName: '服务费用(元)',
    field: 'serviceFee',
    minWidth: 150,
    flex: 1,
    editable: false,
    valueGetter: params => calcServiceFee(params.data),
    aggFunc: decimalAggSum,
    // valueFormatter: formatEditableNumber
  },
  {
    headerName: '说明',
    field: 'remark',
    minWidth: 180,
    flex: 1.2,
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },

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



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)
  } 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 mydiyTheme = myTheme.withParams({
  rowBorder: {
    style: "solid",
    width: 0.8,
    color: "#d8d8dd"
  },
  columnBorder: {
    style: "solid",
    width: 0.8,
    color: "#d8d8dd"
  }
})
</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"
          :columnDefs="columnDefs" :gridOptions="gridOptions" :theme="mydiyTheme" :treeData="false"
          :enableCellSpan="true"
          @cell-value-changed="handleCellValueChanged" :suppressColumnVirtualisation="true"
          :suppressRowVirtualisation="true" 
         
       
  
  :enableClipboard="true"
          :localeText="AG_GRID_LOCALE_CN" :tooltipShowDelay="500" :headerHeight="50"
          :processCellForClipboard="processCellForClipboard" :processCellFromClipboard="processCellFromClipboard"
          :undoRedoCellEditing="true" :undoRedoCellEditingLimit="20" />
      </div>
    </div>
  </div>
</template>
   <!-- :rowSelection="'multiple'"   
  :enableClickSelection="false" -->
   <!-- :suppressRowTransform="true" -->
