/**
 * 工作量法计算模块
 *
 * 提供工作量法的行构建、合并、费用计算等纯函数，
 * 供 WorkloadPricingPane.vue 和 pricingMethodTotals.ts 共用。
 */

import { taskList } from '@/sql'
import { roundTo, toDecimal, toFiniteNumberOrNull } from '@/lib/decimal'
import { getDefaultConsultCategoryFactor } from '@/lib/pricingScaleCalc'
import type { WorkloadCalcRow, TaskLite } from '@/types/pricing'

/* ----------------------------------------------------------------
 * 任务字典查询
 * ---------------------------------------------------------------- */

/** 获取指定咨询服务下的任务条目（按 ID 排序） */
export const getTaskEntriesByServiceId = (serviceId: string | number): [string, TaskLite][] =>
  Object.entries(taskList as Record<string, TaskLite>)
    .filter(([, task]) => Number(task.serviceID) === Number(serviceId))
    .sort((a, b) => Number(a[0]) - Number(b[0]))

/** 判断指定服务是否有工作量法任务 */
export const hasWorkloadTasks = (serviceId: string | number): boolean =>
  getTaskEntriesByServiceId(serviceId).length > 0

/** 格式化任务参考单价范围文本 */
export const formatTaskReferenceUnitPrice = (task: TaskLite): string => {
  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 ''
}

/* ----------------------------------------------------------------
 * 行构建与合并
 * ---------------------------------------------------------------- */

/** 构建工作量法默认行 */
export const buildDefaultWorkloadRows = (
  serviceId: string | number,
  consultCategoryFactorMap?: Map<string, number | null>
): WorkloadCalcRow[] => {
  const defaultFactor =
    consultCategoryFactorMap?.get(String(serviceId)) ?? getDefaultConsultCategoryFactor(serviceId)
  return getTaskEntriesByServiceId(serviceId).map(([taskId, task], order) => ({
    id: `task-${taskId}-${order}`,
    conversion: toFiniteNumberOrNull(task.conversion),
    workload: null,
    basicFee: null,
    budgetAdoptedUnitPrice: toFiniteNumberOrNull(task.defPrice),
    consultCategoryFactor: defaultFactor
  }))
}

/** 合并持久化行与默认行 */
export const mergeWorkloadRows = (
  serviceId: string | number,
  rowsFromDb: Array<Partial<WorkloadCalcRow> & Pick<WorkloadCalcRow, 'id'>> | undefined,
  consultCategoryFactorMap?: Map<string, number | null>
): WorkloadCalcRow[] => {
  const dbMap = new Map<string, Partial<WorkloadCalcRow> & Pick<WorkloadCalcRow, 'id'>>()
  for (const row of rowsFromDb || []) dbMap.set(row.id, row)

  return buildDefaultWorkloadRows(serviceId, consultCategoryFactorMap).map(row => {
    const fromDb = dbMap.get(row.id)
    if (!fromDb) return row
    const hasConsultCategoryFactor = Object.prototype.hasOwnProperty.call(fromDb, 'consultCategoryFactor')
    return {
      ...row,
      workload: toFiniteNumberOrNull(fromDb.workload),
      basicFee: toFiniteNumberOrNull(fromDb.basicFee),
      budgetAdoptedUnitPrice: toFiniteNumberOrNull(fromDb.budgetAdoptedUnitPrice),
      consultCategoryFactor:
        toFiniteNumberOrNull(fromDb.consultCategoryFactor)
        ?? (hasConsultCategoryFactor ? null : row.consultCategoryFactor)
    }
  })
}

/* ----------------------------------------------------------------
 * 费用计算
 * ---------------------------------------------------------------- */

/** 计算工作量法基本费用 = 采用单价 × 换算系数 × 工作量 */
export const calcWorkloadBasicFee = (row: WorkloadCalcRow): number | null => {
  const { budgetAdoptedUnitPrice, conversion, workload } = row
  if (budgetAdoptedUnitPrice == null || conversion == null || workload == null) return null
  return roundTo(toDecimal(budgetAdoptedUnitPrice).mul(conversion).mul(workload), 2)
}

/** 计算工作量法服务费用 = 基本费用 × 咨询分类系数 */
export const calcWorkloadServiceFee = (row: WorkloadCalcRow): number | null => {
  if (row.consultCategoryFactor == null) return null
  const basicFee = row.basicFee ?? calcWorkloadBasicFee(row)
  if (basicFee == null) return null
  return roundTo(toDecimal(basicFee).mul(row.consultCategoryFactor), 2)
}
