<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef, FirstDataRenderedEvent, GridApi, GridReadyEvent, GridSizeChangedEvent } from 'ag-grid-community'
import { myTheme, gridOptions, agGridWrapClass, agGridStyle } from '@/lib/diyAgGridOptions'
import { parseNumberOrNull } from '@/lib/number'
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
import { useKvStore } from '@/pinia/kv'

interface DictItem {
  code: string
  name: string
  defCoe: number | null
  desc?: string | null
  notshowByzxflxs?: boolean
  order?: number | null
}

interface FactorRow {
  id: string
  code: string
  name: string
  standardFactor: number | null
  budgetValue: number | null
  remark: string
  path: string[]
}

interface GridState {
  detailRows: FactorRow[]
}

type DictSource = Record<string, DictItem>

const props = defineProps<{
  title: string
  storageKey: string
  parentStorageKey?: string
  dict: DictSource
  disableBudgetEditWhenStandardNull?: boolean
  excludeNotshowByZxflxs?: boolean
  initBudgetValueFromStandard?: boolean
}>()

const kvStore = useKvStore()
const detailRows = ref<FactorRow[]>([])
const gridApi = ref<GridApi<FactorRow> | null>(null)

const formatReadonlyFactor = (value: unknown) => {
  if (value == null || value === '') return ''
  const parsed = parseNumberOrNull(value, { precision: 3 })
  if (parsed == null) return ''
  return String(Number(parsed))
}

const formatEditableFactor = (params: any) => {
  if (params.value == null || params.value === '') return '点击输入'
  const parsed = parseNumberOrNull(params.value, { precision: 3 })
  if (parsed == null) return ''
  return String(Number(parsed))
}

const sortedDictEntries = () =>
  Object.entries(props.dict)
    .filter((entry): entry is [string, DictItem] => {
      const item = entry[1]
      if (!item?.code || !item?.name) return false
      if (props.excludeNotshowByZxflxs && item.notshowByzxflxs === true) return false
      return true
    })
    .sort((a, b) => {
      const aOrder = Number(a[1]?.order)
      const bOrder = Number(b[1]?.order)
      if (Number.isFinite(aOrder) && Number.isFinite(bOrder) && aOrder !== bOrder) return aOrder - bOrder
      if (Number.isFinite(aOrder) && !Number.isFinite(bOrder)) return -1
      if (!Number.isFinite(aOrder) && Number.isFinite(bOrder)) return 1
      return String(a[1]?.code || a[0]).localeCompare(String(b[1]?.code || b[0]))
    })

const buildCodePath = (code: string, selfId: string, codeIdMap: Map<string, string>) => {
  const parts = code.split('-').filter(Boolean)
  if (!parts.length) return [selfId]

  const path: string[] = []
  let currentCode = parts[0]
  const firstId = codeIdMap.get(currentCode)
  if (firstId) path.push(firstId)

  for (let i = 1; i < parts.length; i += 1) {
    currentCode = `${currentCode}-${parts[i]}`
    const id = codeIdMap.get(currentCode)
    if (id) path.push(id)
  }

  if (!path.length || path[path.length - 1] !== selfId) path.push(selfId)
  return path
}

const buildDefaultRows = (): FactorRow[] => {
  const entries = sortedDictEntries()
  const codeIdMap = new Map<string, string>()
  for (const [id, item] of entries) {
    codeIdMap.set(item.code, id)
  }

  return entries.map(([id, item]) => {
    const standardFactor = typeof item.defCoe === 'number' && Number.isFinite(item.defCoe) ? item.defCoe : null
    return {
      id,
      code: item.code,
      name: item.name,
      standardFactor,
      budgetValue: props.initBudgetValueFromStandard ? standardFactor : null,
      remark: '',
      path: buildCodePath(item.code, id, codeIdMap)
    }
  })
}

type SourceRow = Pick<FactorRow, 'id'> & Partial<Pick<FactorRow, 'budgetValue' | 'remark'>>
const hasMeaningfulFactorValue = (rows: SourceRow[] | undefined) =>
  Array.isArray(rows) &&
  rows.some(row => {
    const hasBudgetValue = typeof row?.budgetValue === 'number' && Number.isFinite(row.budgetValue)
    const hasRemark = typeof row?.remark === 'string' && row.remark.trim() !== ''
    return hasBudgetValue || hasRemark
  })

const mergeWithDictRows = (rowsFromDb: SourceRow[] | undefined): FactorRow[] => {
  const dbValueMap = new Map<string, SourceRow>()
  for (const row of rowsFromDb || []) {
    dbValueMap.set(row.id, row)
  }

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

    const hasBudgetValue = Object.prototype.hasOwnProperty.call(fromDb, 'budgetValue')
    const hasRemark = Object.prototype.hasOwnProperty.call(fromDb, 'remark')
    return {
      ...row,
      budgetValue:
        typeof fromDb.budgetValue === 'number'
          ? fromDb.budgetValue
          : hasBudgetValue
            ? null
            : row.budgetValue,
      remark: typeof fromDb.remark === 'string' ? fromDb.remark : hasRemark ? '' : row.remark
    }
  })
}

const columnDefs: ColDef<FactorRow>[] = [
  {
    headerName: '标准系数',
    field: 'standardFactor',
    minWidth: 86,
    maxWidth: 100,
    headerClass: 'ag-right-aligned-header',
    cellClass: 'ag-right-aligned-cell',
    flex: 0.9,
    valueFormatter: params => formatReadonlyFactor(params.value)
  },
  {
    headerName: '预算取值',
    field: 'budgetValue',
    minWidth: 86,
    maxWidth: 100,
    headerClass: 'ag-right-aligned-header',
    cellClass: params => {
      const disabled = props.disableBudgetEditWhenStandardNull && params.data?.standardFactor == null
      return disabled ? 'ag-right-aligned-cell' : 'ag-right-aligned-cell editable-cell-line'
    },
    flex: 0.9,
    cellClassRules: {
      'editable-cell-empty': params => {
        const disabled = props.disableBudgetEditWhenStandardNull && params.data?.standardFactor == null
        return !disabled && (params.value == null || params.value === '')
      }
    },
    editable: params => {
      if (!props.disableBudgetEditWhenStandardNull) return true
      return params.data?.standardFactor != null
    },
    valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
    valueFormatter: params => {
      const disabled = props.disableBudgetEditWhenStandardNull && params.data?.standardFactor == null
      if (disabled && (params.value == null || params.value === '')) return ''
      return formatEditableFactor(params)
    }
  },
  {
    headerName: '说明',
    field: 'remark',
    minWidth: 170,
    flex: 2.4,
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },
    editable: true,
    valueFormatter: params => params.value || '点击输入',
    cellClass: ' remark-wrap-cell',
    cellClassRules: {
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  }
]

const autoGroupColumnDef: ColDef<FactorRow> = {
  headerName: '专业编码以及工程专业名称',
  minWidth: 220,
  flex: 2.2,
  cellRendererParams: {
    suppressCount: true
  },
  valueFormatter: params => {
    if (params.data?.code && params.data?.name) return `${params.data.code} ${params.data.name}`
    const key = String(params.node?.key || '')
    const dictItem = (props.dict as DictSource)[key]
    return dictItem ? `${dictItem.code} ${dictItem.name}` : ''
  }
}

const saveToIndexedDB = async () => {
  try {
    const payload: GridState = {
      detailRows: JSON.parse(JSON.stringify(detailRows.value))
    }
    await kvStore.setItem(props.storageKey, payload)
  } catch (error) {
    console.error('saveToIndexedDB failed:', error)
  }
}

const loadGridState = async (storageKey: string): Promise<GridState | null> => {
  if (!storageKey) return null
  const data = await kvStore.getItem<GridState>(storageKey)
  if (!data?.detailRows || !Array.isArray(data.detailRows)) return null
  return data
}

const loadFromIndexedDB = async () => {
  try {
    const data = await loadGridState(props.storageKey)
    if (data && hasMeaningfulFactorValue(data.detailRows)) {
      detailRows.value = mergeWithDictRows(data.detailRows)
      return
    }

    const parentStorageKey = props.parentStorageKey?.trim()
    if (parentStorageKey) {
      const parentData = await loadGridState(parentStorageKey)
      if (parentData && hasMeaningfulFactorValue(parentData.detailRows)) {
        detailRows.value = mergeWithDictRows(parentData.detailRows)
        await saveToIndexedDB()
        return
      }
    }

    if (data) {
      detailRows.value = mergeWithDictRows(data.detailRows)
      return
    }

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

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

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 (_error) {
    return params.value
  }
  return params.value
}


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

watch(
  () => props.dict,
  () => {
    void loadFromIndexedDB()
  },
  { deep: true }
)

onBeforeUnmount(() => {
  if (gridPersistTimer) clearTimeout(gridPersistTimer)
  gridApi.value = 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">{{ title }}</h3>
        <div class="text-xs text-muted-foreground"></div>
      </div>

      <div :class="agGridWrapClass">
        <AgGridVue
          :style="agGridStyle"
          :rowData="detailRows"
          :columnDefs="columnDefs"
          :autoGroupColumnDef="autoGroupColumnDef"
          :gridOptions="gridOptions"
          :theme="myTheme"
          :animateRows="true"
          :treeData="true"
          @cell-value-changed="handleCellValueChanged"
          :suppressColumnVirtualisation="true"
          :suppressRowVirtualisation="true"
          :cellSelection="{ handle: { mode: 'range' } }"
          :enableClipboard="true"
          :localeText="AG_GRID_LOCALE_CN"
          :tooltipShowDelay="500"
          :headerHeight="50"
          :suppressHorizontalScroll="true"
          :processCellForClipboard="processCellForClipboard"
          :processCellFromClipboard="processCellFromClipboard"
          :undoRedoCellEditing="true"
          :undoRedoCellEditingLimit="20"
       
        />
      </div>
    </div>
  </div>
</template>
