<script setup lang="ts">
import { computed, onActivated, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import type { CellValueChangedEvent, ColDef, GridApi, GridReadyEvent } from 'ag-grid-community'
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { decimalAggSum, roundTo, sumByNumber } from '@/lib/decimal'
import { formatThousandsFlexible } from '@/lib/numberFormat'
import { industryTypeList, getMajorDictEntries, isMajorIdInIndustryScope } from '@/sql'
import { SwitchRoot, SwitchThumb } from 'reka-ui'
import { useKvStore } from '@/pinia/kv'



interface DictLeaf {
  id: string
  code: string
  name: string
  hasCost: boolean

  hasArea: boolean
}

interface DictGroup {
  id: string
  code: string
  name: string
  children: DictLeaf[]
}



interface XmScaleState {
  detailRows?: DetailRow[]
  totalAmount: number
  roughCalcEnabled: boolean

}
interface XmBaseInfoState {
  projectIndustry?: string
}

const XM_SCALE_FLUSH_EVENT = 'jgjs:xm-scale-flush-request'
type MajorLite = { code: string; name: string; hasCost?: boolean; hasArea?: boolean }
const kvStore = useKvStore()

const detailRows = ref<DetailRow[]>([])
const detailDict = ref<DictGroup[]>([])

const majorEntries = getMajorDictEntries().map(({ id, item }) => [id, item] as [string, MajorLite])
const majorIdAliasMap = new Map(getMajorDictEntries().map(({ rawId, id }) => [rawId, id]))

const buildDetailDict = (entries: Array<[string, MajorLite]>) => {
  const groupMap = new Map<string, DictGroup>()
  const groupOrder: string[] = []
  const codeLookup = new Map(entries.map(([key, item]) => [item.code, { id: key, ...item }]))

  for (const [key, item] of entries) {
    const isGroup = !item.code.includes('-')
    if (isGroup) {
      if (!groupMap.has(item.code)) groupOrder.push(item.code)
      groupMap.set(item.code, {
        id: key,
        code: item.code,
        name: item.name,
        children: []
      })
      continue
    }

    const parentCode = item.code.split('-')[0]
    if (!groupMap.has(parentCode)) {
      const parent = codeLookup.get(parentCode)
      if (!groupOrder.includes(parentCode)) groupOrder.push(parentCode)
      groupMap.set(parentCode, {
        id: parent?.id || `group-${parentCode}`,
        code: parentCode,
        name: parent?.name || parentCode,
        children: []
      })
    }
    groupMap.get(parentCode)!.children.push({
      id: key,
      code: item.code,
      name: item.name,

      hasCost: item.hasCost !== false,
      hasArea: item.hasArea !== false
    })
  }

  return groupOrder.map(code => groupMap.get(code)).filter((group): group is DictGroup => Boolean(group))
}



const buildDefaultRows = (): DetailRow[] => {
  const rows: DetailRow[] = []
  for (const group of detailDict.value) {
    for (const child of group.children) {

      rows.push({
        id: child.id,
        groupCode: group.code,
        groupName: group.name,
        majorCode: child.code,
        majorName: child.name,
        hasCost: child.hasCost,
        hasArea: child.hasArea,
        amount: null,
        landArea: null,
        path: [`${group.code} ${group.name}`, `${child.code} ${child.name}`]
      })
    }
  }
  return rows
}

const mergeWithDictRows = (rowsFromDb: DetailRow[] | undefined): DetailRow[] => {
  const dbValueMap = new Map<string, DetailRow>()
  for (const row of rowsFromDb || []) {
    if (row?.isGroupRow === true) continue
    const rowId = String(row.id)
    dbValueMap.set(rowId, row)
    const aliasId = majorIdAliasMap.get(rowId)
    if (aliasId && !dbValueMap.has(aliasId)) {
      dbValueMap.set(aliasId, row)
    }
  }

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

    return {
      ...row,
      hide: fromDb.hide,
      amount: row.hasCost && typeof fromDb.amount === 'number' ? fromDb.amount : null,
      landArea: row.hasArea && typeof fromDb.landArea === 'number' ? fromDb.landArea : null
    }
  })
}

const buildGroupRows = (rows: DetailRow[]): DetailRow[] => {
  const rowById = new Map(rows.map(row => [String(row.id || ''), row] as const))
  const groupRows: DetailRow[] = []
  for (const group of detailDict.value) {
    let amountTotal = 0
    let hasAmount = false
    let landAreaTotal = 0
    let hasLandArea = false
    for (const child of group.children) {
      const leaf = rowById.get(String(child.id || ''))
      const amount = leaf?.amount
      if (typeof amount === 'number' && Number.isFinite(amount)) {
        amountTotal += amount
        hasAmount = true
      }
      const landArea = leaf?.landArea
      if (typeof landArea === 'number' && Number.isFinite(landArea)) {
        landAreaTotal += landArea
        hasLandArea = true
      }
    }
    groupRows.push({
      id: group.id,
      groupCode: group.code,
      groupName: group.name,
      majorCode: group.code,
      majorName: group.name,
      hasCost: true,
      hasArea: true,
      amount: hasAmount ? roundTo(amountTotal, 3) : null,
      landArea: hasLandArea ? roundTo(landAreaTotal, 3) : null,
      path: [`${group.code} ${group.name}`],
      hide: false,
      isGroupRow: true
    })
  }
  return groupRows
}


const applyPinnedTotalAmount = (
  api: GridApi<DetailRow> | null | undefined,
  totalAmount: number | null | undefined
) => {
  const normalized = typeof totalAmount === 'number' && Number.isFinite(totalAmount)
    ? roundTo(totalAmount, 2)
    : null
  pinnedTopRowData.value[0].amount = normalized
  const pinnedTopNode = api?.getPinnedTopRow(0)
  if (pinnedTopNode) {
    pinnedTopNode.setDataValue('amount', normalized)
  }
}

const loadFromIndexedDB = async (api: GridApi<DetailRow>) => {
  try {
    const [baseInfo, contractData] = await Promise.all([
      kvStore.getItem<XmBaseInfoState>(props.baseInfoKey || 'xm-base-info-v1'),
      kvStore.getItem<XmScaleState>(props.dbKey)
    ])

    activeIndustryId.value =
      typeof baseInfo?.projectIndustry === 'string' ? baseInfo.projectIndustry.trim() : ''

    if (!activeIndustryId.value) {
      detailDict.value = []
      detailRows.value = []
      roughCalcEnabled.value = false
      applyPinnedTotalAmount(api, null)
      return
    }

    const filteredEntries = majorEntries.filter(([id]) =>
      isMajorIdInIndustryScope(id, activeIndustryId.value)
    )
    detailDict.value = buildDetailDict(filteredEntries)

    roughCalcEnabled.value = Boolean(contractData?.roughCalcEnabled)
    applyPinnedTotalAmount(api, contractData?.totalAmount)
    const contractRows = Array.isArray(contractData?.detailRows) ? contractData.detailRows : []
    const hasContractRows = contractRows.length > 0
    const hasContractScaleValue = hasContractRows
      ? contractRows.some(row => {
          const amount = row?.amount
          const landArea = row?.landArea
          return (
            (typeof amount === 'number' && Number.isFinite(amount)) ||
            (typeof landArea === 'number' && Number.isFinite(landArea))
          )
        })
      : false
    const isLegacyEmptyScaleRows =
      hasContractRows &&
      !hasContractScaleValue &&
      !roughCalcEnabled.value &&
      typeof contractData?.totalAmount === 'number' &&
      Number.isFinite(contractData.totalAmount)
    if (hasContractRows && !isLegacyEmptyScaleRows) {
      detailRows.value = mergeWithDictRows(contractRows)
      return
    }

    if (props.xmInfoKey) {
      // 首次创建合同段时，默认继承项目规模信息（同一套专业字典，按 id 对齐）
      const xmData = await kvStore.getItem<XmScaleState>(props.xmInfoKey)
      roughCalcEnabled.value = Boolean(xmData?.roughCalcEnabled)
      applyPinnedTotalAmount(api, xmData?.totalAmount)

      if (Array.isArray(xmData?.detailRows) && xmData.detailRows.length > 0) {
        detailRows.value = mergeWithDictRows(xmData.detailRows)
        return
      }
    }
    detailRows.value = buildDefaultRows()


    void saveToIndexedDB()
  } catch (error) {
    console.error('loadFromIndexedDB failed:', error)
    activeIndustryId.value = ''
    detailRows.value = []
    roughCalcEnabled.value = false
    applyPinnedTotalAmount(api, null)
  }
}

interface DetailRow {
  id: string
  groupCode: string
  groupName: string
  majorCode: string
  majorName: string
  hasCost: boolean
  hasArea: boolean
  amount: number | null
  landArea: number | null
  path: string[]
  hide?: boolean
  isGroupRow?: boolean
}

interface XmBaseInfoState {
  projectIndustry?: string
}

interface GridPersistState {
  detailRows?: DetailRow[]
  roughCalcEnabled?: boolean
  totalAmount?: number | null
}

const props = defineProps<{
  title: string
  dbKey: string
  xmInfoKey?: string | null
  baseInfoKey?: string
}>()

let persistTimer: ReturnType<typeof setTimeout> | null = null
const gridApi = ref<GridApi<DetailRow> | null>(null)
const activeIndustryId = ref('')
const industryNameMap = new Map(
  industryTypeList.flatMap(item => [
    [String(item.id).trim(), item.name],
    [String(item.type).trim(), item.name]
  ])
)
const totalLabel = computed(() => {
  const industryName = industryNameMap.get(activeIndustryId.value.trim()) || ''
  return industryName ? `${industryName}总投资` : '总投资'
})
const roughCalcEnabled = ref(false)
const visibleRowData = computed(() => { return detailRows.value.filter(row => !row.hide) })

const refreshPinnedTotalLabelCell = () => {
  if (!gridApi.value) return
  const pinnedTopNode = gridApi.value.getPinnedTopRow(0)
  if (!pinnedTopNode) return
  gridApi.value.refreshCells({
    rowNodes: [pinnedTopNode],
    force: true
  })
}

const columnDefs: ColDef<DetailRow>[] = [
  {
    headerName: '造价金额(万元)',
    field: 'amount',
    headerClass: 'ag-right-aligned-header',
    minWidth: 100,
    flex: 1,
    editable: params => {
      if (roughCalcEnabled.value) return Boolean(params.node?.rowPinned)
      return !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost)
    },
    cellClass: params =>
      roughCalcEnabled.value && params.node?.rowPinned
        ? 'ag-right-aligned-cell editable-cell-line'
        : !roughCalcEnabled.value && !params.node?.group && !params.node?.rowPinned && params.data?.hasCost
          ? 'ag-right-aligned-cell editable-cell-line'
          : 'ag-right-aligned-cell',
    cellClassRules: {
      'editable-cell-empty': params =>
        roughCalcEnabled.value && params.node?.rowPinned
          ? params.value == null || params.value === ''
          : !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasCost) && (params.value == null || params.value === '')
    },
    aggFunc: decimalAggSum,
    valueParser: params => {
      if (params.newValue === '' || params.newValue == null) return null
      const v = Number(params.newValue)
      return Number.isFinite(v) ? roundTo(v, 3) : null
    },
    valueFormatter: params => {
      if (roughCalcEnabled.value) {
        if (!params.node?.rowPinned) return ''
        if (params.value == null || params.value === '') return '点击输入'
        return formatThousandsFlexible(params.value, 3)
      }
      if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasCost) {
        return ''
      }
      if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
        return '点击输入'
      }
      if (params.value == null) return ''
      return formatThousandsFlexible(params.value, 3)
    }
  },
  {
    headerName: '用地面积(亩)',
    field: 'landArea',
    headerClass: 'ag-right-aligned-header',
    minWidth: 100,
    flex: 1,
    editable: params => !roughCalcEnabled.value && !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea),
    cellClass: params =>
      !params.node?.group && !params.node?.rowPinned && params.data?.hasArea
        ? 'ag-right-aligned-cell editable-cell-line'
        : 'ag-right-aligned-cell',
    cellClassRules: {
      'editable-cell-empty': params =>
        !params.node?.group && !params.node?.rowPinned && Boolean(params.data?.hasArea) && (params.value == null || params.value === '')
    },
    valueParser: params => {
      if (params.newValue === '' || params.newValue == null) return null
      const v = Number(params.newValue)
      return Number.isFinite(v) ? roundTo(v, 3) : null
    },
    valueFormatter: params => {
      if (roughCalcEnabled.value) {
        return ''
      }
      if (!params.node?.group && !params.node?.rowPinned && !params.data?.hasArea) {
        return ''
      }
      if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
        return '点击输入'
      }
      if (params.value == null) return ''
      return formatThousandsFlexible(params.value, 3)
    }
  }
]

const autoGroupColumnDef: ColDef = {
  headerName: '专业编码以及工程专业名称',
  minWidth: 200,
  flex: 2,
  cellRendererParams: {
    suppressCount: true
  },
  valueFormatter: params => {
    if (params.node?.rowPinned) return totalLabel.value
    return String(params.value || '')
  },
  tooltipValueGetter: params => {
    if (params.node?.rowPinned) return totalLabel.value
    return String(params.value || '')
  }
}

const pinnedTopRowData = ref<DetailRow[]>([
  {
    id: 'pinned-total-row',
    groupCode: '',
    groupName: '',
    majorCode: '',
    majorName: '',
    hasCost: false,
    hasArea: false,
    amount: null,
    landArea: null,
    path: ['TOTAL']
  }
])

const saveToIndexedDB = async () => {
  try {
    const leafRows = detailRows.value.map(row => ({
      ...JSON.parse(JSON.stringify(row)),
      hide: Boolean(row.hide),
      isGroupRow: false
    }))
    const totalAmountFromRows = (() => {
      let hasValue = false
      let total = 0
      for (const row of leafRows) {
        const amount = row?.amount
        if (typeof amount !== 'number' || !Number.isFinite(amount)) continue
        total += amount
        hasValue = true
      }
      return hasValue ? roundTo(total, 2) : null
    })()
    const pinnedAmount = pinnedTopRowData.value[0].amount
    const normalizedPinnedAmount =
      typeof pinnedAmount === 'number' && Number.isFinite(pinnedAmount)
        ? roundTo(pinnedAmount, 2)
        : null
    const normalizedTotalAmount = roughCalcEnabled.value ? normalizedPinnedAmount : totalAmountFromRows
    pinnedTopRowData.value[0].amount = normalizedTotalAmount
    const payload: GridPersistState = {
      detailRows: [...leafRows, ...buildGroupRows(leafRows)]
    }
    payload.roughCalcEnabled = roughCalcEnabled.value
    payload.totalAmount = normalizedTotalAmount
    await kvStore.setItem(props.dbKey, payload)
  } catch (error) {
    console.error('saveToIndexedDB failed:', error)
  }
}

const schedulePersist = () => {
  if (persistTimer) clearTimeout(persistTimer)
  persistTimer = setTimeout(() => {
    void saveToIndexedDB()
  }, 600)
}

const handleFlushPersistRequest = (event: Event) => {
  const customEvent = event as CustomEvent<{ done?: () => void }>
  const done = customEvent?.detail?.done
  if (persistTimer) {
    clearTimeout(persistTimer)
    persistTimer = null
  }
  void saveToIndexedDB().finally(() => {
    done?.()
  })
}

const setDetailRowsHidden = (hidden: boolean) => {
  for (const row of detailRows.value) {
    row.hide = hidden
  }
}

let oldValue:number|null
const onRoughCalcSwitch = (checked: boolean) => {
  gridApi.value?.stopEditing(true)
  roughCalcEnabled.value = checked
  setDetailRowsHidden(checked)
  if (!checked) {
    oldValue=pinnedTopRowData.value[0].amount
    syncPinnedTotalForNormalMode()
  } else {
    pinnedTopRowData.value[0].amount = oldValue
    const pinnedTopNode = gridApi.value?.getPinnedTopRow(0)
    if (pinnedTopNode) {
      pinnedTopNode.setDataValue('amount', oldValue)
    }
  }
  schedulePersist()
}



const onCellValueChanged = (event: CellValueChangedEvent) => {
  if (roughCalcEnabled.value && event.node?.rowPinned && event.colDef.field === 'amount') {
    if (typeof event.newValue === 'number') {
      pinnedTopRowData.value[0].amount = roundTo(event.newValue, 2)
    } else {
      const parsed = Number(event.newValue)
      pinnedTopRowData.value[0].amount = Number.isFinite(parsed) ? roundTo(parsed, 2) : null
    }

  } else if (!roughCalcEnabled.value) {
    syncPinnedTotalForNormalMode()
  }
  schedulePersist()
}

const onGridReady = (event: GridReadyEvent<DetailRow>) => {
  gridApi.value = event.api

  void loadFromIndexedDB(event.api)

  void refreshPinnedTotalLabelCell()

}

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) {
    // no-op
  }
  return params.value
}







watch(totalLabel, () => {
  refreshPinnedTotalLabelCell()
})

const syncPinnedTotalForNormalMode = () => {
  if (roughCalcEnabled.value) return

  if (!gridApi.value) {
    pinnedTopRowData.value[0].amount = sumByNumber(detailRows.value, row => row.amount)
    return
  }
  let total = 0
  let hasValue = false

  detailRows.value.forEach(node => {

    const amount = node.amount
    if (typeof amount === 'number' && Number.isFinite(amount)) {
      total += amount
      hasValue = true
    }
  })
  pinnedTopRowData.value[0].amount = hasValue ? roundTo(total, 2) : null
  const pinnedTopNode = gridApi.value.getPinnedTopRow(0)
  if (pinnedTopNode) {
    pinnedTopNode.setDataValue('amount', hasValue ? roundTo(total, 2) : null)
  }
}

onBeforeUnmount(() => {
  if (persistTimer) clearTimeout(persistTimer)
  window.removeEventListener(XM_SCALE_FLUSH_EVENT, handleFlushPersistRequest as EventListener)
  gridApi.value = null
  void saveToIndexedDB()
})

onMounted(() => {
  window.addEventListener(XM_SCALE_FLUSH_EVENT, handleFlushPersistRequest as EventListener)
})
</script>

<template>
  <div class="h-full">
    <div class="rounded-lg border bg-card xmMx scroll-mt-3 flex flex-col overflow-hidden h-full">
      <div class="flex items-center justify-between border-b px-4 py-3">
        <h3
          class="text-sm font-semibold text-foreground cursor-pointer select-none transition-colors hover:text-primary">
          {{ props.title }}
        </h3>
        <div class="flex items-center gap-2">
          <span class=" text-xs text-muted-foreground">简要计算</span>
          <SwitchRoot
            class="cursor-pointer peer h-5 w-9 shrink-0 rounded-full border border-transparent bg-muted shadow-sm transition-colors data-[state=checked]:bg-primary"
            :modelValue="roughCalcEnabled" @update:modelValue="onRoughCalcSwitch">
            <SwitchThumb
              class="block h-4 w-4 translate-x-0.5 rounded-full bg-background shadow transition-transform data-[state=checked]:translate-x-4" />
          </SwitchRoot>
        </div>
      </div>

      <div class="ag-theme-quartz w-full flex-1 min-h-0 h-full">
        <AgGridVue :style="{ height: '100%' }" :rowData="visibleRowData" :pinnedTopRowData="pinnedTopRowData"
          :columnDefs="columnDefs" :autoGroupColumnDef="autoGroupColumnDef" :gridOptions="gridOptions" :theme="myTheme"
          @grid-ready="onGridReady" @cell-value-changed="onCellValueChanged" :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>
