import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
  addNumbers,
  roundTo,
  sumNullableNumbers,
  toFiniteNumberOrNull,
  toFiniteNumberOrZero
} from '@/lib/decimal'
import { waitForHydration } from '@/pinia/Plugin/indexdb'
import {
  parseHtFeeMainStorageKey,
  parseHtFeeMethodStorageKey,
  useZxFwPricingHtFeeStore
} from '@/pinia/zxFwPricingHtFee'
import { useZxFwPricingKeysStore } from '@/pinia/zxFwPricingKeys'

export type ZxFwPricingField = 'investScale' | 'landScale' | 'serviceFee' | 'hourly'
export type ServicePricingMethod = ZxFwPricingField

export interface ZxFwDetailRow {
  id: string
  code?: string
  name?: string
  remark?: string
  process?: number | null
  investScale: number | null
  landScale: number | null
  serviceFee: number | null
  hourly: number | null
  subtotal?: number | null
  finalFee?: number | null
  actions?: unknown
}

export interface ZxFwState {
  selectedIds?: string[]
  selectedCodes?: string[]
  detailRows: ZxFwDetailRow[]
}

export interface ServicePricingMethodState<TRow = unknown> {
  detailRows: TRow[]
  projectCount?: number | null
}

export interface ServicePricingState {
  investScale?: ServicePricingMethodState
  landScale?: ServicePricingMethodState
  serviceFee?: ServicePricingMethodState
  hourly?: ServicePricingMethodState
}

export type HtFeeMethodType = 'rate-fee' | 'hourly-fee' | 'quantity-unit-price-fee'

export interface HtFeeMainState<TRow = unknown> {
  detailRows: TRow[]
}

export type HtFeeMethodPayload = unknown

const FIXED_ROW_ID = 'fixed-budget-c'
const METHOD_STORAGE_PREFIX_MAP: Record<ServicePricingMethod, string> = {
  investScale: 'tzGMF',
  landScale: 'ydGMF',
  serviceFee: 'gzlF',
  hourly: 'hourlyPricing'
}
const STORAGE_PREFIX_METHOD_MAP = new Map<string, ServicePricingMethod>(
  Object.entries(METHOD_STORAGE_PREFIX_MAP).map(([method, prefix]) => [prefix, method as ServicePricingMethod])
)

const toKey = (contractId: string | number) => String(contractId || '').trim()
const toServiceKey = (serviceId: string | number) => String(serviceId || '').trim()
const serviceMethodDbKeyOf = (contractId: string, serviceId: string, method: ServicePricingMethod) =>
  `${METHOD_STORAGE_PREFIX_MAP[method]}-${contractId}-${serviceId}`
const round3Nullable = (value: number | null | undefined) => {
  const numeric = toFiniteNumberOrNull(value)
  return numeric == null ? null : roundTo(numeric, 3)
}
const normalizeProcessValue = (value: unknown, rowId: string) => {
  if (rowId === FIXED_ROW_ID) return null
  return Number(value) === 1 ? 1 : 0
}
const toKeySnapshot = (value: unknown) => JSON.stringify(value ?? null)
const cloneAny = <T>(value: T): T => {
  if (value == null) return value
  return JSON.parse(JSON.stringify(value)) as T
}
interface HtFeeMainDetailRowLike {
  id?: unknown
  rateFee?: unknown
  hourlyFee?: unknown
  quantityUnitPriceFee?: unknown
  subtotal?: unknown
  [key: string]: unknown
}
interface HtRateMethodStateLike {
  rate?: unknown
  budgetFee?: unknown
  [key: string]: unknown
}
interface HtHourlyMethodRowLike {
  adoptedBudgetUnitPrice?: unknown
  personnelCount?: unknown
  workdayCount?: unknown
  serviceBudget?: unknown
}
interface HtHourlyMethodStateLike {
  detailRows?: HtHourlyMethodRowLike[]
}
interface HtQuantityMethodRowLike {
  id?: unknown
  budgetFee?: unknown
  quantity?: unknown
  unitPrice?: unknown
}
interface HtQuantityMethodStateLike {
  detailRows?: HtQuantityMethodRowLike[]
}

const normalizeRows = (rows: unknown): ZxFwDetailRow[] =>
  (Array.isArray(rows) ? rows : []).map(item => {
    const row = item as Partial<ZxFwDetailRow>
    const rowId = String(row.id || '')
    return {
      id: rowId,
      code: typeof row.code === 'string' ? row.code : '',
      name: typeof row.name === 'string' ? row.name : '',
      remark: typeof row.remark === 'string' ? row.remark : '',
      process: normalizeProcessValue(row.process, rowId),
      investScale: toFiniteNumberOrNull(row.investScale),
      landScale: toFiniteNumberOrNull(row.landScale),
      serviceFee: toFiniteNumberOrNull(row.serviceFee),
      hourly: toFiniteNumberOrNull(row.hourly),
      subtotal: toFiniteNumberOrNull(row.subtotal),
      finalFee: toFiniteNumberOrNull(row.finalFee),
      actions: row.actions
    }
  }).filter(row => row.id)

const applyRowSubtotals = (rows: ZxFwDetailRow[]): ZxFwDetailRow[] => {
  const normalized = rows.map(row => ({ ...row }))
  const nonFixedRows = normalized.filter(row => row.id !== FIXED_ROW_ID)
  const totalInvestScale = sumNullableNumbers(nonFixedRows.map(row => row.investScale))
  const totalLandScale = sumNullableNumbers(nonFixedRows.map(row => row.landScale))
  const totalWorkload = sumNullableNumbers(nonFixedRows.map(row => row.workload))
  const totalHourly = sumNullableNumbers(nonFixedRows.map(row => row.hourly))
  const fixedSubtotal = sumNullableNumbers([totalInvestScale, totalLandScale, totalWorkload, totalHourly])
  return normalized.map(row => {
    if (row.id === FIXED_ROW_ID) {
      return {
        ...row,
        investScale: round3Nullable(totalInvestScale),
        landScale: round3Nullable(totalLandScale),
        workload: round3Nullable(totalWorkload),
        hourly: round3Nullable(totalHourly),
        subtotal: round3Nullable(fixedSubtotal),
        finalFee: row.finalFee
      }
    }
    const subtotal = sumNullableNumbers([
      row.investScale,
      row.landScale,
      row.workload,
      row.hourly
    ])
    return {
      ...row,
      subtotal: round3Nullable(subtotal),
      finalFee: row.finalFee != null ? round3Nullable(row.finalFee) : round3Nullable(subtotal)
    }
  })
}

const normalizeState = (state: ZxFwState | null | undefined): ZxFwState => ({
  selectedIds: Array.isArray(state?.selectedIds)
    ? state.selectedIds.map(id => String(id || '')).filter(Boolean)
    : [],
  selectedCodes: Array.isArray(state?.selectedCodes)
    ? state.selectedCodes.map(code => String(code || '')).filter(Boolean)
    : [],
  detailRows: applyRowSubtotals(normalizeRows(state?.detailRows))
})

const cloneState = (state: ZxFwState): ZxFwState => ({
  selectedIds: [...(state.selectedIds || [])],
  selectedCodes: [...(state.selectedCodes || [])],
  detailRows: state.detailRows.map(row => ({ ...row }))
})

const isSameStringArray = (a: string[] | undefined, b: string[] | undefined) => {
  const left = Array.isArray(a) ? a : []
  const right = Array.isArray(b) ? b : []
  if (left.length !== right.length) return false
  for (let i = 0; i < left.length; i += 1) {
    if (left[i] !== right[i]) return false
  }
  return true
}

const isSameNullableNumber = (a: number | null | undefined, b: number | null | undefined) => {
  const left = toFiniteNumberOrNull(a)
  const right = toFiniteNumberOrNull(b)
  return left === right
}

const isSameRows = (a: ZxFwDetailRow[] | undefined, b: ZxFwDetailRow[] | undefined) => {
  const left = Array.isArray(a) ? a : []
  const right = Array.isArray(b) ? b : []
  if (left.length !== right.length) return false
  for (let i = 0; i < left.length; i += 1) {
    const l = left[i]
    const r = right[i]
    if (!l || !r) return false
    if (l.id !== r.id) return false
    if ((l.code || '') !== (r.code || '')) return false
    if ((l.name || '') !== (r.name || '')) return false
    if ((l.remark || '') !== (r.remark || '')) return false
    if (normalizeProcessValue(l.process, l.id) !== normalizeProcessValue(r.process, r.id)) return false
    if (!isSameNullableNumber(l.investScale, r.investScale)) return false
    if (!isSameNullableNumber(l.landScale, r.landScale)) return false
    if (!isSameNullableNumber(l.workload, r.workload)) return false
    if (!isSameNullableNumber(l.hourly, r.hourly)) return false
    if (!isSameNullableNumber(l.subtotal, r.subtotal)) return false
    if (!isSameNullableNumber(l.finalFee, r.finalFee)) return false
  }
  return true
}

const isSameState = (a: ZxFwState | null | undefined, b: ZxFwState | null | undefined) => {
  if (!a || !b) return false
  if (!isSameStringArray(a.selectedIds, b.selectedIds)) return false
  if (!isSameStringArray(a.selectedCodes, b.selectedCodes)) return false
  return isSameRows(a.detailRows, b.detailRows)
}

const normalizeProjectCount = (value: unknown) => {
  const numeric = Number(value)
  if (!Number.isFinite(numeric)) return null
  return Math.max(1, Math.floor(numeric))
}

const normalizeServiceMethodState = (
  payload: Partial<ServicePricingMethodState> | null | undefined
): ServicePricingMethodState => ({
  detailRows: Array.isArray(payload?.detailRows) ? cloneAny(payload.detailRows) : [],
  projectCount: normalizeProjectCount(payload?.projectCount)
})

const parseServiceMethodStorageKey = (keyRaw: string | number) => {
  const key = toKey(keyRaw)
  if (!key) return null
  const firstDash = key.indexOf('-')
  if (firstDash <= 0 || firstDash >= key.length - 1) return null
  const prefix = key.slice(0, firstDash)
  const method = STORAGE_PREFIX_METHOD_MAP.get(prefix)
  if (!method) return null
  const rest = key.slice(firstDash + 1)
  const splitIndex = rest.lastIndexOf('-')
  if (splitIndex <= 0 || splitIndex >= rest.length - 1) return null
  const contractId = rest.slice(0, splitIndex).trim()
  const serviceId = rest.slice(splitIndex + 1).trim()
  if (!contractId || !serviceId) return null
  return { key, method, contractId, serviceId }
}

const loadTasks = new Map<string, Promise<ZxFwState | null>>()

export const useZxFwPricingStore = defineStore('zxFwPricing', () => {
  let hydrationReady = false
  let hydrationTask: Promise<void> | null = null
  const contracts = ref<Record<string, ZxFwState>>({})
  const contractLoaded = ref<Record<string, boolean>>({})
  const servicePricingStates = ref<Record<string, Record<string, ServicePricingState>>>({})

  const keysStore = useZxFwPricingKeysStore()
  const htFeeStore = useZxFwPricingHtFeeStore()

  const htFeeMainStates = htFeeStore.htFeeMainStates
  const htFeeMethodStates = htFeeStore.htFeeMethodStates
  const keyedStates = keysStore.keyedStates

  const ensureHydrated = async () => {
    if (hydrationReady) return
    if (!hydrationTask) {
      hydrationTask = waitForHydration('zxFwPricing')
        .catch(() => undefined)
        .finally(() => {
          hydrationReady = true
          hydrationTask = null
        })
    }
    await hydrationTask
  }

  const ensureServicePricingState = (contractIdRaw: string | number, serviceIdRaw: string | number) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return null
    if (!servicePricingStates.value[contractId]) {
      servicePricingStates.value[contractId] = {}
    }
    if (!servicePricingStates.value[contractId][serviceId]) {
      servicePricingStates.value[contractId][serviceId] = {}
    }
    return servicePricingStates.value[contractId][serviceId]
  }

  const setServiceMethodStateInMemory = (
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod,
    payload: Partial<ServicePricingMethodState> | null | undefined
  ) => {
    const state = ensureServicePricingState(contractIdRaw, serviceIdRaw)
    if (!state) return null
    if (!payload) {
      delete state[method]
      return null
    }
    state[method] = normalizeServiceMethodState(payload)
    return state[method] || null
  }

  const getServicePricingMethodState = <TRow = unknown>(
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod
  ) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return null
    return (servicePricingStates.value[contractId]?.[serviceId]?.[method] as ServicePricingMethodState<TRow> | undefined) || null
  }

  // 写入某合同某服务某种计费方式的明细状态。
  // 默认会同步更新 keysStore，从而触发持久化；syncKeyState=false 时只回填内存态。
  const setServicePricingMethodState = <TRow = unknown>(
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod,
    payload: Partial<ServicePricingMethodState<TRow>> | null | undefined,
    options?: {
      force?: boolean
      syncKeyState?: boolean
    }
  ) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return false

    const storageKey = serviceMethodDbKeyOf(contractId, serviceId, method)
    const force = options?.force === true
    const syncKeyState = options?.syncKeyState !== false
    const normalizedPayload = payload == null ? null : normalizeServiceMethodState(payload)
    const prevSnapshot = toKeySnapshot(getServicePricingMethodState(contractId, serviceId, method))
    const nextSnapshot = toKeySnapshot(normalizedPayload)
    if (!force && prevSnapshot === nextSnapshot) return false

    setServiceMethodStateInMemory(contractId, serviceId, method, normalizedPayload)

    if (syncKeyState) {
      if (normalizedPayload == null) {
        keysStore.removeKeyState(storageKey)
      } else {
        keysStore.setKeyState(storageKey, cloneAny(normalizedPayload), { force: true })
      }
    }
    return true
  }

  // 按需加载某个服务的计费方式明细。
  // 若内存中已有且未强制刷新，直接返回；否则从 keysStore/KV 读取后回填到内存。
  const loadServicePricingMethodState = async <TRow = unknown>(
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod,
    force = false
  ): Promise<ServicePricingMethodState<TRow> | null> => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return null

    if (!force) {
      const existing = getServicePricingMethodState<TRow>(contractId, serviceId, method)
      if (existing) return existing
    }

    const storageKey = serviceMethodDbKeyOf(contractId, serviceId, method)
    const payload = await loadKeyState<ServicePricingMethodState<TRow>>(storageKey, force)
    if (!payload) {
      setServiceMethodStateInMemory(contractId, serviceId, method, null)
      return null
    }
    setServicePricingMethodState(contractId, serviceId, method, payload, { force: true, syncKeyState: false })
    return getServicePricingMethodState<TRow>(contractId, serviceId, method)
  }

  // 删除单个服务某种计费方式的状态，并同步清理对应持久化 key。
  const removeServicePricingMethodState = (
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod
  ) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return false
    const storageKey = serviceMethodDbKeyOf(contractId, serviceId, method)
    const had = getServicePricingMethodState(contractId, serviceId, method) != null
    setServiceMethodStateInMemory(contractId, serviceId, method, null)
    keysStore.removeKeyState(storageKey)
    return had
  }

  // 暴露给外部使用的单个计费方式存储键，便于兼容旧逻辑直接读写底层数据。
  const getServicePricingStorageKey = (
    contractIdRaw: string | number,
    serviceIdRaw: string | number,
    method: ServicePricingMethod
  ) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return ''
    return serviceMethodDbKeyOf(contractId, serviceId, method)
  }

  // 返回某个服务全部计费方式对应的存储键集合，常用于批量清理或导出。
  const getServicePricingStorageKeys = (contractIdRaw: string | number, serviceIdRaw: string | number) => {
    const contractId = toKey(contractIdRaw)
    const serviceId = toServiceKey(serviceIdRaw)
    if (!contractId || !serviceId) return [] as string[]
    return (Object.keys(METHOD_STORAGE_PREFIX_MAP) as ServicePricingMethod[]).map(method =>
      serviceMethodDbKeyOf(contractId, serviceId, method)
    )
  }

  const removeAllServicePricingMethodStates = (contractIdRaw: string | number, serviceIdRaw: string | number) => {
    let changed = false
    for (const method of Object.keys(METHOD_STORAGE_PREFIX_MAP) as ServicePricingMethod[]) {
      changed = removeServicePricingMethodState(contractIdRaw, serviceIdRaw, method) || changed
    }
    return changed
  }

  const getHtFeeMainState = htFeeStore.getHtFeeMainState
  const setHtFeeMainState = htFeeStore.setHtFeeMainState
  const loadHtFeeMainState = htFeeStore.loadHtFeeMainState
  const removeHtFeeMainState = htFeeStore.removeHtFeeMainState
  const getHtFeeMethodStorageKey = htFeeStore.getHtFeeMethodStorageKey
  const getHtFeeMethodState = htFeeStore.getHtFeeMethodState
  const setHtFeeMethodState = htFeeStore.setHtFeeMethodState
  const loadHtFeeMethodState = htFeeStore.loadHtFeeMethodState
  const removeHtFeeMethodState = htFeeStore.removeHtFeeMethodState

  const sumHourlyMethodFee = (state: HtHourlyMethodStateLike | null): number | null => {
    const rows = Array.isArray(state?.detailRows)
      ? state.detailRows.filter(row => toFiniteNumberOrNull(row?.serviceBudget) != null)
      : []
    if (rows.length === 0) return null

    let total = 0
    let hasValid = false
    for (const row of rows) {
      const serviceBudget = toFiniteNumberOrNull(row?.serviceBudget)
      if (serviceBudget != null) {
        total += serviceBudget
        hasValid = true
        continue
      }
      const adopted = toFiniteNumberOrNull(row?.adoptedBudgetUnitPrice)
      const personnel = toFiniteNumberOrNull(row?.personnelCount)
      const workday = toFiniteNumberOrNull(row?.workdayCount)
      if (adopted == null || personnel == null || workday == null) continue
      total += adopted * personnel * workday
      hasValid = true
    }
    return hasValid ? roundTo(total, 3) : null
  }

  const sumQuantityMethodFee = (state: HtQuantityMethodStateLike | null): number | null => {
    const rows = Array.isArray(state?.detailRows)
      ? state.detailRows.filter(row => toFiniteNumberOrNull(row?.budgetFee) != null)
      : []
    if (rows.length === 0) return null

    let total = 0
    let hasValid = false
    for (const row of rows) {
      if (String(row?.id || '') === 'fee-subtotal-fixed') continue
      const budget = toFiniteNumberOrNull(row?.budgetFee)
      if (budget != null) {
        total += budget
        hasValid = true
        continue
      }
      const quantity = toFiniteNumberOrNull(row?.quantity)
      const unitPrice = toFiniteNumberOrNull(row?.unitPrice)
      if (quantity == null || unitPrice == null) continue
      total += quantity * unitPrice
      hasValid = true
    }
    return hasValid ? roundTo(total, 3) : null
  }

  const getHtRowSubtotal = (row: HtFeeMainDetailRowLike | null | undefined): number | null => {
    if (!row) return null
    const values = [
      toFiniteNumberOrNull(row.rateFee),
      toFiniteNumberOrNull(row.hourlyFee),
      toFiniteNumberOrNull(row.quantityUnitPriceFee)
    ]
    if (!values.some(value => value != null)) return null
    let sum = 0
    for (const value of values) {
      sum += toFiniteNumberOrZero(value)
    }
    return roundTo(sum, 3)
  }

  const sumMainStateSubtotal = (rows: HtFeeMainDetailRowLike[]): number | null => {
    if (!Array.isArray(rows) || rows.length === 0) return null
    const subtotalValues = rows.map(row => getHtRowSubtotal(row)).filter(value => value != null) as number[]
    if (subtotalValues.length === 0) return null
    return roundTo(subtotalValues.reduce((sum, value) => sum + value, 0), 3)
  }

  const syncHtMainByBase = async (
    mainStorageKey: string,
    baseValue: number | null
  ): Promise<number | null> => {
    const mainState = await loadHtFeeMainState<HtFeeMainDetailRowLike>(mainStorageKey)
    const sourceRows = Array.isArray(mainState?.detailRows) ? mainState.detailRows : []
    if (sourceRows.length === 0) return null

    const nextRows = await Promise.all(
      sourceRows.map(async sourceRow => {
        const rowId = String(sourceRow?.id || '').trim()
        if (!rowId) return sourceRow

        const [rateState, hourlyState, quantityState] = await Promise.all([
          loadHtFeeMethodState<HtRateMethodStateLike>(mainStorageKey, rowId, 'rate-fee'),
          loadHtFeeMethodState<HtHourlyMethodStateLike>(mainStorageKey, rowId, 'hourly-fee'),
          loadHtFeeMethodState<HtQuantityMethodStateLike>(mainStorageKey, rowId, 'quantity-unit-price-fee')
        ])

        const rateValue = toFiniteNumberOrNull(rateState?.rate)
        const nextRateFee = baseValue != null && rateValue != null ? roundTo(baseValue * rateValue / 100, 2) : null
        if (rateState) {
          const currentRateFee = toFiniteNumberOrNull(rateState.budgetFee)
          if (currentRateFee !== nextRateFee) {
            setHtFeeMethodState(
              mainStorageKey,
              rowId,
              'rate-fee',
              {
                ...rateState,
                budgetFee: nextRateFee
              },
              { force: true }
            )
          }
        }

        const nextHourlyFee = sumHourlyMethodFee(hourlyState)
        const nextQuantityFee = sumQuantityMethodFee(quantityState)
        const nextRow = {
          ...sourceRow,
          rateFee: nextRateFee,
          hourlyFee: nextHourlyFee,
          quantityUnitPriceFee: nextQuantityFee
        }
        return {
          ...nextRow,
          subtotal: getHtRowSubtotal(nextRow)
        }
      })
    )

    const prevSnapshot = toKeySnapshot(sourceRows)
    const nextSnapshot = toKeySnapshot(nextRows)
    if (prevSnapshot !== nextSnapshot) {
      setHtFeeMainState(mainStorageKey, { detailRows: nextRows }, { force: true })
      return sumMainStateSubtotal(nextRows)
    }
    return sumMainStateSubtotal(sourceRows)
  }

  const syncHtExtraFeeByContractBase = async (contractId: string) => {
    if (!contractId) return
    try {
      const serviceBase = getBaseSubtotal(contractId)
      const additionalKey = `htExtraFee-${contractId}-additional-work`
      const reserveKey = `htExtraFee-${contractId}-reserve`
      const additionalTotal = await syncHtMainByBase(additionalKey, serviceBase)

      const hasReserveBase = serviceBase != null || additionalTotal != null
      const reserveBase = hasReserveBase
        ? roundTo(toFiniteNumberOrZero(serviceBase) + toFiniteNumberOrZero(additionalTotal), 3)
        : null
      await syncHtMainByBase(reserveKey, reserveBase)
    } catch (error) {
      console.error('syncHtExtraFeeByContractBase failed:', error)
    }
  }

  const getKeyState = <T = unknown>(keyRaw: string | number): T | null => {
    const key = toKey(keyRaw)
    if (!key) return null
    const serviceMeta = parseServiceMethodStorageKey(key)
    if (serviceMeta) {
      const methodState = getServicePricingMethodState(
        serviceMeta.contractId,
        serviceMeta.serviceId,
        serviceMeta.method
      )
      if (methodState != null) return cloneAny(methodState as T)
    }
    const htMethodMeta = parseHtFeeMethodStorageKey(key)
    if (htMethodMeta) {
      const methodState = getHtFeeMethodState(
        htMethodMeta.mainStorageKey,
        htMethodMeta.rowId,
        htMethodMeta.method
      )
      if (methodState != null) return cloneAny(methodState as T)
    }
    const htMainMeta = parseHtFeeMainStorageKey(key)
    if (htMainMeta) {
      const mainState = getHtFeeMainState(htMainMeta.mainStorageKey)
      if (mainState != null) return cloneAny(mainState as T)
    }
    return keysStore.getKeyState<T>(key)
  }

  const loadKeyState = async <T = unknown>(keyRaw: string | number, force = false): Promise<T | null> => {
    const key = toKey(keyRaw)
    if (!key) return null

    const raw = await keysStore.loadKeyState<T>(key, force)

    const serviceMeta = parseServiceMethodStorageKey(key)
    if (serviceMeta) {
      setServicePricingMethodState(
        serviceMeta.contractId,
        serviceMeta.serviceId,
        serviceMeta.method,
        raw as Partial<ServicePricingMethodState>,
        { force: true, syncKeyState: false }
      )
    }

    const htMethodMeta = parseHtFeeMethodStorageKey(key)
    if (htMethodMeta) {
      setHtFeeMethodState(
        htMethodMeta.mainStorageKey,
        htMethodMeta.rowId,
        htMethodMeta.method,
        raw,
        { force: true, syncKeyState: false }
      )
    }

    const htMainMeta = parseHtFeeMainStorageKey(key)
    if (htMainMeta) {
      setHtFeeMainState(htMainMeta.mainStorageKey, raw as Partial<HtFeeMainState>, { force: true, syncKeyState: false })
    }

    return getKeyState<T>(key)
  }

  const setKeyState = <T = unknown>(
    keyRaw: string | number,
    value: T,
    options?: {
      force?: boolean
    }
  ) => {
    const key = toKey(keyRaw)
    if (!key) return false

    const serviceMeta = parseServiceMethodStorageKey(key)
    if (serviceMeta) {
      setServicePricingMethodState(
        serviceMeta.contractId,
        serviceMeta.serviceId,
        serviceMeta.method,
        value as Partial<ServicePricingMethodState>,
        { force: true, syncKeyState: false }
      )
    }

    const htMethodMeta = parseHtFeeMethodStorageKey(key)
    if (htMethodMeta) {
      setHtFeeMethodState(
        htMethodMeta.mainStorageKey,
        htMethodMeta.rowId,
        htMethodMeta.method,
        value,
        { force: true, syncKeyState: false }
      )
    }

    const htMainMeta = parseHtFeeMainStorageKey(key)
    if (htMainMeta) {
      setHtFeeMainState(htMainMeta.mainStorageKey, value as Partial<HtFeeMainState>, { force: true, syncKeyState: false })
    }

    return keysStore.setKeyState(key, value, options)
  }

  const removeKeyState = (keyRaw: string | number) => {
    const key = toKey(keyRaw)
    if (!key) return false

    const serviceMeta = parseServiceMethodStorageKey(key)
    if (serviceMeta) {
      setServiceMethodStateInMemory(serviceMeta.contractId, serviceMeta.serviceId, serviceMeta.method, null)
    }

    const htMethodMeta = parseHtFeeMethodStorageKey(key)
    if (htMethodMeta) {
      setHtFeeMethodState(htMethodMeta.mainStorageKey, htMethodMeta.rowId, htMethodMeta.method, null, {
        force: true,
        syncKeyState: false
      })
    }

    const htMainMeta = parseHtFeeMainStorageKey(key)
    if (htMainMeta) {
      setHtFeeMainState(htMainMeta.mainStorageKey, null, { force: true, syncKeyState: false })
    }

    return keysStore.removeKeyState(key)
  }

  // 对外返回合同咨询服务状态的深拷贝，避免组件直接改写 store 内部引用。
  const getContractState = (contractIdRaw: string | number) => {
    const contractId = toKey(contractIdRaw)
    if (!contractId) return null
    const data = contracts.value[contractId]
    return data ? cloneState(data) : null
  }

  // 加载合同维度的咨询服务汇总状态。
  // 同一合同在并发场景下会复用同一个加载任务，避免重复读取 KV。
  const loadContract = async (contractIdRaw: string | number, force = false) => {
    const contractId = toKey(contractIdRaw)
    if (!contractId) return null
    await ensureHydrated()
    if (!force && contractLoaded.value[contractId]) return getContractState(contractId)
    if (!force && contracts.value[contractId]) return getContractState(contractId)
    if (!force && loadTasks.has(contractId)) return loadTasks.get(contractId) as Promise<ZxFwState | null>

    const task = (async () => {
      const current = contracts.value[contractId]
      if (!current) {
        contracts.value[contractId] = normalizeState(null)
      }
      contractLoaded.value[contractId] = true
      return getContractState(contractId)
    })()
    loadTasks.set(contractId, task)

    try {
      return await task
    } finally {
      loadTasks.delete(contractId)
    }
  }

  // 整体替换某个合同的咨询服务状态。
  // 入口会先标准化数据并做快照比较，只有真实变化时才递增版本号。
  const setContractState = async (contractIdRaw: string | number, state: ZxFwState) => {
    const contractId = toKey(contractIdRaw)
    if (!contractId) return false
    const normalized = normalizeState(state)
    const current = contracts.value[contractId]
    if (current && isSameState(current, normalized)) return false
    contracts.value[contractId] = normalized
    contractLoaded.value[contractId] = true
    return true
  }

  // 只更新某个服务行上的单个汇总字段，适合计费页回写 investScale/landScale/workload/hourly。
  // 为保证“计价法金额变化 -> 确认金额跟随小计”，这里会同步重算 finalFee：
  // - 普通行：finalFee = 当前四种计价法小计
  // - 固定小计行：finalFee = 普通行 finalFee 合计
  const updatePricingField = async (params: {
    contractId: string
    serviceId: string | number
    field: ZxFwPricingField
    value: number | null | undefined
  }) => {
    const contractId = toKey(params.contractId)
    if (!contractId) return false

    const current = contracts.value[contractId]
    if (!current?.detailRows?.length) return false

    const targetServiceId = String(params.serviceId || '').trim()
    if (!targetServiceId) return false

    const nextValue = toFiniteNumberOrNull(params.value)

    let changed = false
    const updatedRows = current.detailRows.map(row => {
      if (String(row.id || '') !== targetServiceId) return row
      const oldValue = toFiniteNumberOrNull(row[params.field])
      if (oldValue === nextValue) return row
      changed = true
      return {
        ...row,
        [params.field]: nextValue
      }
    })

    if (!changed) return false

    const rowsWithSyncedFinalFee = updatedRows.map(row => {
      const rowId = String(row.id || '')
      if (rowId === FIXED_ROW_ID) return row
      if (rowId !== targetServiceId) return row
      const rowSubtotal = sumNullableNumbers([
        toFiniteNumberOrNull(row.investScale),
        toFiniteNumberOrNull(row.landScale),
        toFiniteNumberOrNull(row.serviceFee),
        toFiniteNumberOrNull(row.hourly)
      ])
      return {
        ...row,
        finalFee: round3Nullable(rowSubtotal)
      }
    })

    const fixedFinalFee = round3Nullable(
      sumNullableNumbers(
        rowsWithSyncedFinalFee
          .filter(row => String(row.id || '') !== FIXED_ROW_ID)
          .map(row => toFiniteNumberOrNull(row.finalFee))
      )
    )

    const nextRows = rowsWithSyncedFinalFee.map(row =>
      String(row.id || '') === FIXED_ROW_ID
        ? {
          ...row,
          finalFee: fixedFinalFee
        }
        : row
    )

    const nextState = normalizeState({
      ...current,
      detailRows: nextRows
    })
    if (isSameState(current, nextState)) return false
    contracts.value[contractId] = nextState
    contractLoaded.value[contractId] = true
    await syncHtExtraFeeByContractBase(contractId)

    return true
  }

  const getBaseSubtotal = (contractIdRaw: string | number): number | null => {
    const contractId = toKey(contractIdRaw)
    if (!contractId) return null
    const state = contracts.value[contractId]
    if (!state?.detailRows?.length) return null
    const fixedRow = state.detailRows.find(row => String(row.id || '') === FIXED_ROW_ID)
    const fixedFinalFee = toFiniteNumberOrNull(fixedRow?.finalFee)
    if (fixedFinalFee != null) return roundTo(fixedFinalFee, 3)

    let hasValid = false
    const sum = state.detailRows.reduce((acc, row) => {
      if (String(row.id || '') === FIXED_ROW_ID) return acc
      const fee = toFiniteNumberOrNull(row.finalFee) ?? toFiniteNumberOrNull(row.subtotal)
      if (fee != null) hasValid = true
      return fee == null ? acc : acc + fee
    }, 0)
    return hasValid ? roundTo(sum, 3) : null
  }

  // 清理合同维度的内存态、版本号以及该合同下所有相关持久化键。
  const removeContractData = (contractIdRaw: string | number) => {
    const contractId = toKey(contractIdRaw)
    if (!contractId) return false
    let changed = false

    if (Object.prototype.hasOwnProperty.call(contracts.value, contractId)) {
      delete contracts.value[contractId]
      changed = true
    }
    if (Object.prototype.hasOwnProperty.call(servicePricingStates.value, contractId)) {
      delete servicePricingStates.value[contractId]
      changed = true
    }
    if (Object.prototype.hasOwnProperty.call(contractLoaded.value, contractId)) {
      delete contractLoaded.value[contractId]
      changed = true
    }
    loadTasks.delete(contractId)

    changed = htFeeStore.removeContractHtFeeData(contractId) || changed

    const htMainPrefix = `htExtraFee-${contractId}-`
    changed = keysStore.removeKeysByPrefix(htMainPrefix) || changed

    for (const prefix of Object.values(METHOD_STORAGE_PREFIX_MAP)) {
      changed = keysStore.removeKeysByPrefix(`${prefix}-${contractId}-`) || changed
    }

    return changed
  }

  return {
    contracts,
    contractLoaded,
    servicePricingStates,
    htFeeMainStates,
    htFeeMethodStates,
    keyedStates,
    getContractState,
    loadContract,
    setContractState,
    updatePricingField,
    getBaseSubtotal,
    removeContractData,
    getKeyState,
    loadKeyState,
    setKeyState,
    removeKeyState,
    getServicePricingMethodState,
    setServicePricingMethodState,
    loadServicePricingMethodState,
    removeServicePricingMethodState,
    getServicePricingStorageKey,
    getServicePricingStorageKeys,
    removeAllServicePricingMethodStates,
    getHtFeeMainState,
    setHtFeeMainState,
    loadHtFeeMainState,
    removeHtFeeMainState,
    getHtFeeMethodStorageKey,
    getHtFeeMethodState,
    setHtFeeMethodState,
    loadHtFeeMethodState,
    removeHtFeeMethodState
  }
}, {
  persist: true
})
