import { serviceList } from '@/sql'
import { roundTo, toFiniteNumber, toFiniteNumberOrZero } from '@/lib/decimal'
import { getBenchmarkBudgetSplitByScale, getScaleBudgetFeeSplit } from '@/lib/pricingScaleFee'
export { toFiniteNumber, toFiniteNumberOrZero }

interface ScaleMethodRowLike {
  id: string
  majorDictId?: unknown
  hasCost?: unknown
  hasArea?: unknown
  isGroupRow?: unknown
  amount: number | null
  landArea: number | null
  benchmarkBudgetBasicChecked?: unknown
  benchmarkBudgetOptionalChecked?: unknown
  basicFormula?: unknown
  optionalFormula?: unknown
  budgetFee?: unknown
  budgetFeeBasic?: unknown
  budgetFeeOptional?: unknown
  consultCategoryFactor?: unknown
  majorFactor?: unknown
  workStageFactor?: unknown
  workRatio?: unknown
  remark?: unknown
}

interface WorkContentRowLike {
  id?: unknown
  content?: unknown
  checked?: unknown
  custom?: unknown
  serviceid?: unknown
  isAddTrigger?: unknown
}

interface FactorRowLike {
  id: string
  standardFactor?: unknown
  budgetValue?: unknown
  remark?: unknown
}

interface ScaleRowLike {
  id: string
  hasCost?: unknown
  hasArea?: unknown
  isGroupRow?: unknown
  amount: number | null
  landArea: number | null
}

interface WorkloadMethodRowLike {
  id: string
  budgetAdoptedUnitPrice?: unknown
  workload?: unknown
  basicFee?: unknown
  consultCategoryFactor?: unknown
  serviceFee?: unknown
  remark?: unknown
}

interface HourlyMethodRowLike {
  id: string
  adoptedBudgetUnitPrice?: unknown
  personnelCount?: unknown
  workdayCount?: unknown
  serviceBudget?: unknown
  remark?: unknown
}

interface ZxFwRowLike {
  id: string
  process?: unknown
  subtotal?: unknown
  finalFee?: unknown
  investScale?: unknown
  landScale?: unknown
  workload?: unknown
  hourly?: unknown
}

interface RateMethodRowLike {
  rate?: unknown
  budgetFee?: unknown
}

interface QuantityMethodRowLike {
  id?: unknown
  feeItem?: unknown
  unit?: unknown
  quantity?: unknown
  unitPrice?: unknown
  budgetFee?: number | null
  remark?: unknown
}

interface ExportScaleRowLike {
  major: number
  cost: number | null
  area: number | null
}

interface ExportServiceCoeLike {
  serviceid: number
  coe: number
  remark: string
}

interface ExportMajorCoeLike {
  majorid: number
  coe: number
  remark: string
}

interface ExportMethod1DetailLike {
  proNum: number
  major: number
  cost: number
  basicFee: number
  basicFormula: string
  basicFee_basic: number
  optionalFormula: string
  basicFee_optional: number
  serviceCoe: number
  majorCoe: number
  processCoe: number
  proportion: number
  fee: number
  remark: string
}

interface ExportMethod2DetailLike {
  proNum: number
  major: number
  area: number
  basicFee: number
  basicFormula: string
  basicFee_basic: number
  optionalFormula: string
  basicFee_optional: number
  serviceCoe: number
  majorCoe: number
  processCoe: number
  proportion: number
  fee: number
  remark: string
}

interface ExportMethod3DetailLike {
  task: number
  price: number
  amount: number
  basicFee: number
  serviceCoe: number
  fee: number
  remark: string
}

interface ExportMethod4DetailLike {
  expert: number
  price: number
  person_num: number
  work_day: number
  fee: number
  remark: string
}

interface ExportMethod5DetailLike {
  name: string
  unit: string
  amount: number
  price: number
  fee: number
  remark: string
}

export const toSafeInteger = (value: unknown): number | null => {
  const num = Number(value)
  if (!Number.isInteger(num)) return null
  if (!Number.isSafeInteger(num)) return null
  return num
}

export const sumNumbers = (values: Array<number | null | undefined>): number =>
  values.reduce<number>(
    (sum, value) => sum + (typeof value === 'number' && Number.isFinite(value) ? value : 0),
    0
  )

export const toMoney = (value: unknown): number => roundTo(toFiniteNumber(value) ?? 0, 2)

export const isNonEmptyString = (value: unknown): value is string =>
  typeof value === 'string' && value.trim().length > 0

export const getTaskIdFromRowId = (value: string): number | null => {
  const match = /^task-(\d+)-\d+$/.exec(value)
  return match ? toSafeInteger(match[1]) : null
}

export const getExpertIdFromRowId = (value: string): number | null => {
  const match = /^expert-(\d+)$/.exec(value)
  return match ? toSafeInteger(match[1]) : null
}

export const hasServiceId = (serviceId: string) =>
  Object.prototype.hasOwnProperty.call(serviceList as Record<string, unknown>, serviceId)

export const sortServiceIdsByDict = (ids: string[]) =>
  [...ids].sort((left, right) => {
    const leftOrder = Number((serviceList as Record<string, any>)[left]?.order)
    const rightOrder = Number((serviceList as Record<string, any>)[right]?.order)
    const safeLeft = Number.isFinite(leftOrder) ? leftOrder : Number.MAX_SAFE_INTEGER
    const safeRight = Number.isFinite(rightOrder) ? rightOrder : Number.MAX_SAFE_INTEGER
    return safeLeft - safeRight
  })

export const mapIndustryCodeToExportIndustry = (value: unknown): number => {
  const raw = typeof value === 'string' ? value.trim().toUpperCase() : ''
  if (!raw) return 0
  if (raw === '0' || raw === 'E2') return 0
  if (raw === '1' || raw === 'E3') return 1
  if (raw === '2' || raw === 'E4') return 2
  return 0
}

export const parseScaleScopedRowId = (rowId: unknown) => {
  const raw = String(rowId || '').trim()
  const scopedMatch = /^(\d+)::(.+)$/.exec(raw)
  if (scopedMatch) {
    return {
      proNum: toSafeInteger(scopedMatch[1]) ?? 1,
      majorPart: String(scopedMatch[2] || '').trim()
    }
  }
  return {
    proNum: 1,
    majorPart: raw
  }
}

export const toScaleMajorId = (row: ScaleMethodRowLike): number | null => {
  const direct = toSafeInteger((row as { majorDictId?: unknown }).majorDictId)
  if (direct != null) return direct
  const parsed = parseScaleScopedRowId(row.id)
  return toSafeInteger(parsed.majorPart)
}

export const toScaleProNum = (row: ScaleMethodRowLike): number => {
  const parsed = parseScaleScopedRowId(row.id)
  return parsed.proNum > 0 ? parsed.proNum : 1
}

const dedupeScaleMethodRows = (rows: ScaleMethodRowLike[] | undefined): ScaleMethodRowLike[] => {
  if (!Array.isArray(rows) || rows.length === 0) return []
  const deduped = new Map<string, ScaleMethodRowLike>()
  for (const row of rows) {
    if (row?.isGroupRow === true) continue
    const major = toScaleMajorId(row)
    if (major == null) continue
    const proNum = toScaleProNum(row)
    // 同一项目+专业出现重复行时，以最后一条为准，兜底旧浏览器内存态重复污染。
    deduped.set(`${proNum}::${major}`, row)
  }
  return Array.from(deduped.values())
}

const isScaleLeafRow = (row: { isGroupRow?: unknown; hasCost?: unknown; hasArea?: unknown } | null | undefined) => {
  if (!row || row.isGroupRow === true) return false
  if (row.hasCost === true || row.hasArea === true) return true
  return row.hasCost == null && row.hasArea == null
}

const isExportableScaleMethodRow = (
  row: ScaleMethodRowLike | null | undefined,
  mode: 'cost' | 'area'
) => {
  if (!isScaleLeafRow(row)) return false
  return mode === 'cost' ? row?.hasCost === true : row?.hasArea === true
}

export const normalizeTaskText = (value: unknown): string => String(value || '').trim()

export const resolveTaskRowServiceId = (row: WorkContentRowLike): number | null =>
  toSafeInteger((row as { serviceid?: unknown })?.serviceid)

export const resolveScaleMethodFee = (row: ScaleMethodRowLike, mode: 'cost' | 'area') => {
  const scaleValue = mode === 'cost' ? toFiniteNumber(row.amount) : toFiniteNumber(row.landArea)
  const benchmarkSplit = getBenchmarkBudgetSplitByScale(scaleValue, mode)
  const basicChecked = row.benchmarkBudgetBasicChecked !== false
  const optionalChecked = row.benchmarkBudgetOptionalChecked !== false
  const allUnchecked = !basicChecked && !optionalChecked
  const benchmarkBudgetBasic = benchmarkSplit ? (basicChecked ? benchmarkSplit.basic : 0) : null
  const benchmarkBudgetOptional = benchmarkSplit ? (optionalChecked ? benchmarkSplit.optional : 0) : null
  const computedSplit = benchmarkSplit
    ? getScaleBudgetFeeSplit({
      benchmarkBudgetBasic,
      benchmarkBudgetOptional,
      majorFactor: row.majorFactor,
      consultCategoryFactor: row.consultCategoryFactor,
      workStageFactor: row.workStageFactor,
      workRatio: row.workRatio
    })
    : null
  const basicFee = allUnchecked ? null : (toFiniteNumber(row.budgetFee) ?? computedSplit?.total ?? null)
  const basicFeeBasic = allUnchecked ? null : (toFiniteNumber(row.budgetFeeBasic) ?? computedSplit?.basic ?? null)
  const basicFeeOptional = allUnchecked ? null : (toFiniteNumber(row.budgetFeeOptional) ?? computedSplit?.optional ?? null)
  const basicFormula = typeof row.basicFormula === 'string' && row.basicFormula.trim()
    ? row.basicFormula
    : (basicChecked ? (benchmarkSplit?.basicFormula ?? '') : '')
  const optionalFormula = typeof row.optionalFormula === 'string' && row.optionalFormula.trim()
    ? row.optionalFormula
    : (optionalChecked ? (benchmarkSplit?.optionalFormula ?? '') : '')
  return {
    basicFee,
    basicFeeBasic,
    basicFeeOptional,
    basicFormula,
    optionalFormula
  }
}

export interface ExportTaskGroupLike {
  serviceid?: number
  text: string[]
}

export const groupWorkContentTasks = (
  rows: WorkContentRowLike[] | undefined,
  options?: { forceUngroup?: boolean }
): ExportTaskGroupLike[] => {
  const source = Array.isArray(rows) ? rows : []
  const selected = source.filter(item => {
    if (item && item.isAddTrigger === true) return false
    const isCustom = Boolean(item?.custom)
    const isChecked = Boolean(item?.checked)
    return isCustom || isChecked
  })
  if (selected.length === 0) return []

  const hasGroup = !options?.forceUngroup && selected.some(item => resolveTaskRowServiceId(item) != null)
  if (!hasGroup) {
    const text = selected
      .map(item => normalizeTaskText(item?.content))
      .filter(Boolean)
    return text.length > 0 ? [{ text }] : []
  }

  const grouped = new Map<number, string[]>()
  const orderedServiceIds: number[] = []
  const ungroupedText: string[] = []
  for (const item of selected) {
    const content = normalizeTaskText(item?.content)
    if (!content) continue
    const serviceid = resolveTaskRowServiceId(item)
    if (serviceid == null) {
      ungroupedText.push(content)
      continue
    }
    if (!grouped.has(serviceid)) {
      grouped.set(serviceid, [])
      orderedServiceIds.push(serviceid)
    }
    grouped.get(serviceid)?.push(content)
  }

  const groupedTasks: ExportTaskGroupLike[] = []
  for (const serviceid of orderedServiceIds) {
    const text = grouped.get(serviceid) || []
    if (text.length === 0) continue
    groupedTasks.push({ serviceid, text })
  }

  if (ungroupedText.length > 0) {
    groupedTasks.push({ text: ungroupedText })
  }
  return groupedTasks
}

export const buildProjectServiceCoes = (rows: FactorRowLike[] | undefined) => {
  if (!Array.isArray(rows)) return []
  return rows
    .map(row => {
      const serviceid = toSafeInteger(row.id)
      const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor)
      if (serviceid == null || coe == null) return null
      return {
        serviceid,
        coe,
        remark: typeof row.remark === 'string' ? row.remark : ''
      }
    })
    .filter((item): item is ExportServiceCoeLike => Boolean(item))
}

export const buildProjectMajorCoes = (rows: FactorRowLike[] | undefined) => {
  if (!Array.isArray(rows)) return []
  return rows
    .map(row => {
      const majorid = toSafeInteger(row.id)
      const coe = toFiniteNumber(row.budgetValue) ?? toFiniteNumber(row.standardFactor)
      if (majorid == null || coe == null) return null
      return {
        majorid,
        coe,
        remark: typeof row.remark === 'string' ? row.remark : ''
      }
    })
    .filter((item): item is ExportMajorCoeLike => Boolean(item))
}

export const toExportScaleRows = (rows: ScaleRowLike[] | undefined) => {
  if (!Array.isArray(rows)) return []
  return rows
    .filter(row => isScaleLeafRow(row))
    .map(row => {
      const majorid = toSafeInteger(row.id)
      if (majorid == null || (row.amount == null && row.landArea == null)) return null
      return {
        major: majorid,
        cost: row.amount,
        area: row.landArea
      }
    })
    .filter((item): item is ExportScaleRowLike => Boolean(item))
}

export const buildMethod1 = (rows: ScaleMethodRowLike[] | undefined) => {
  const normalizedRows = dedupeScaleMethodRows(rows)
  if (normalizedRows.length === 0) return null
  let hasTotalValue = false
  const proSet = new Set<number>()
  const det = normalizedRows
    .map(row => {
      if (!isExportableScaleMethodRow(row, 'cost')) return null
      const major = toScaleMajorId(row)
      if (major == null) return null
      const proNum = toScaleProNum(row)
      proSet.add(proNum)
      const cost = toFiniteNumber(row.amount)
      const feeResolved = resolveScaleMethodFee(row, 'cost')
      const basicFee = feeResolved.basicFee
      if (basicFee != null) hasTotalValue = true
      const basicFeeBasic = feeResolved.basicFeeBasic
      const basicFeeOptional = feeResolved.basicFeeOptional
      const remark = typeof row.remark === 'string' ? row.remark : ''
      if (basicFee == null) return null
      return {
        proNum,
        major,
        cost: cost ?? 0,
        basicFee: toMoney(basicFee),
        basicFormula: feeResolved.basicFormula,
        basicFee_basic: toMoney(basicFeeBasic),
        optionalFormula: feeResolved.optionalFormula,
        basicFee_optional: toMoney(basicFeeOptional),
        serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
        majorCoe: toFiniteNumberOrZero(row.majorFactor),
        processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
        proportion: toFiniteNumber(row.workRatio) ?? 1,
        fee: toMoney(basicFee),
        remark
      }
    })
    .filter((item): item is ExportMethod1DetailLike => Boolean(item))

  if (det.length === 0 || !hasTotalValue) return null
  return {
    proAmount: proSet.size > 0 ? proSet.size : 1,
    cost: sumNumbers(det.map(item => item.cost)),
    basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))),
    basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))),
    basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))),
    fee: toMoney(sumNumbers(det.map(item => item.fee))),
    det
  }
}

export const buildMethod2 = (rows: ScaleMethodRowLike[] | undefined) => {
  const normalizedRows = dedupeScaleMethodRows(rows)
  if (normalizedRows.length === 0) return null
  let hasTotalValue = false
  const proSet = new Set<number>()
  const det = normalizedRows
    .map(row => {
      if (!isExportableScaleMethodRow(row, 'area')) return null
      const major = toScaleMajorId(row)
      if (major == null) return null
      const proNum = toScaleProNum(row)
      proSet.add(proNum)
      const area = toFiniteNumber(row.landArea)
      const feeResolved = resolveScaleMethodFee(row, 'area')
      const basicFee = feeResolved.basicFee
      if (basicFee != null) hasTotalValue = true
      const basicFeeBasic = feeResolved.basicFeeBasic
      const basicFeeOptional = feeResolved.basicFeeOptional
      const remark = typeof row.remark === 'string' ? row.remark : ''
      if (basicFee == null) return null
      return {
        proNum,
        major,
        area: area ?? 0,
        basicFee: toMoney(basicFee),
        basicFormula: feeResolved.basicFormula,
        basicFee_basic: toMoney(basicFeeBasic),
        optionalFormula: feeResolved.optionalFormula,
        basicFee_optional: toMoney(basicFeeOptional),
        serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
        majorCoe: toFiniteNumberOrZero(row.majorFactor),
        processCoe: toFiniteNumber(row.workStageFactor) ?? 1,
        proportion: toFiniteNumber(row.workRatio) ?? 1,
        fee: toMoney(basicFee),
        remark
      }
    })
    .filter((item): item is ExportMethod2DetailLike => Boolean(item))

  if (det.length === 0 || !hasTotalValue) return null
  return {
    proAmount: proSet.size > 0 ? proSet.size : 1,
    area: sumNumbers(det.map(item => item.area)),
    basicFee: toMoney(sumNumbers(det.map(item => item.basicFee))),
    basicFee_basic: toMoney(sumNumbers(det.map(item => item.basicFee_basic))),
    basicFee_optional: toMoney(sumNumbers(det.map(item => item.basicFee_optional))),
    fee: toMoney(sumNumbers(det.map(item => item.fee))),
    det
  }
}

export const buildMethod3 = (rows: WorkloadMethodRowLike[] | undefined) => {
  if (!Array.isArray(rows)) return null
  let hasTotalValue = false
  const det = rows
    .map(row => {
      const task = getTaskIdFromRowId(row.id)
      if (task == null || row.basicFee == null) return null
      const amount = toFiniteNumber(row.workload)
      const basicFee = toFiniteNumber(row.basicFee)
      const fee = toFiniteNumber(row.serviceFee)
      if (fee != null) hasTotalValue = true
      const remark = typeof row.remark === 'string' ? row.remark : ''
      const hasValue = amount != null || basicFee != null || fee != null || isNonEmptyString(remark)
      if (!hasValue) return null
      return {
        task,
        price: toFiniteNumberOrZero(row.budgetAdoptedUnitPrice),
        amount: amount ?? 0,
        basicFee: basicFee ?? 0,
        serviceCoe: toFiniteNumberOrZero(row.consultCategoryFactor),
        fee: fee ?? 0,
        remark
      }
    })
    .filter((item): item is ExportMethod3DetailLike => Boolean(item))

  if (det.length === 0 || !hasTotalValue) return null
  return {
    basicFee: sumNumbers(det.map(item => item.basicFee)),
    fee: sumNumbers(det.map(item => item.fee)),
    det
  }
}

export const buildMethod4 = (rows: HourlyMethodRowLike[] | undefined) => {
  if (!Array.isArray(rows)) return null
  let hasTotalValue = false
  const det = rows
    .map(row => {
      const expert = getExpertIdFromRowId(row.id)
      if (expert == null || row.serviceBudget == null) return null
      const personNum = toFiniteNumber(row.personnelCount)
      const workDay = toFiniteNumber(row.workdayCount)
      const fee = toFiniteNumber(row.serviceBudget)
      if (fee != null) hasTotalValue = true
      const remark = typeof row.remark === 'string' ? row.remark : ''
      const hasValue = personNum != null || workDay != null || fee != null || isNonEmptyString(remark)
      if (!hasValue) return null
      return {
        expert,
        price: toFiniteNumberOrZero(row.adoptedBudgetUnitPrice),
        person_num: personNum ?? 0,
        work_day: workDay ?? 0,
        fee: fee ?? 0,
        remark
      }
    })
    .filter((item): item is ExportMethod4DetailLike => Boolean(item))

  if (det.length === 0 || !hasTotalValue) return null
  return {
    person_num: sumNumbers(det.map(item => item.person_num)),
    work_day: sumNumbers(det.map(item => item.work_day)),
    fee: sumNumbers(det.map(item => item.fee)),
    det
  }
}

export const buildServiceFee = (
  row: ZxFwRowLike | null | undefined,
  method1: { fee: number } | null,
  method2: { fee: number } | null,
  method3: { fee: number } | null,
  method4: { fee: number } | null
) => {
  const subtotal = toFiniteNumber(row?.subtotal)
  return subtotal != null ? toMoney(subtotal) : 0
}

export const buildMethod0 = (payload: RateMethodRowLike | null | undefined) => {
  const coe = toFiniteNumber(payload?.rate)
  const fee = toFiniteNumber(payload?.budgetFee)
  if (fee == null) return null
  return {
    coe: coe ?? 0,
    fee
  }
}

export const buildMethod5 = (rows: QuantityMethodRowLike[] | undefined) => {
  if (!Array.isArray(rows) || rows.length === 0) return null
  const subtotalRow = rows.find(row => String(row?.id || '') === 'fee-subtotal-fixed')
  const subtotalFee = toFiniteNumber(subtotalRow?.budgetFee)
  if (subtotalFee == null) return null
  const det = rows
    .filter(row => String(row?.id || '') !== 'fee-subtotal-fixed')
    .map(row => {
      const quantity = toFiniteNumber(row.quantity)
      const unitPrice = toFiniteNumber(row.unitPrice)
      const fee = toFiniteNumber(row.budgetFee)
      const name = typeof row.feeItem === 'string' ? row.feeItem : ''
      const unit = typeof row.unit === 'string' ? row.unit : ''
      const remark = typeof row.remark === 'string' ? row.remark : ''

      if (row.budgetFee == null) return null
      return {
        name,
        unit,
        amount: quantity ?? 0,
        price: unitPrice ?? 0,
        fee: fee ?? 0,
        remark
      }
    })
    .filter((item): item is ExportMethod5DetailLike => Boolean(item))

  if (det.length === 0) return null
  return {
    fee: subtotalFee,
    det
  }
}

export const buildServiceFinalFee = (
  row: ZxFwRowLike | null | undefined,
  method1: { fee: number } | null,
  method2: { fee: number } | null,
  method3: { fee: number } | null,
  method4: { fee: number } | null
) => {
  const finalFee = toFiniteNumber(row?.finalFee)
  if (finalFee != null) return toMoney(finalFee)
  const subtotal = toFiniteNumber(row?.subtotal)
  return subtotal != null ? toMoney(subtotal) : 0
}
