const EXPENSE_TYPE_LABELS = {
  travel: '差旅费',
  hotel: '住宿费',
  transport: '交通费',
  meal: '业务招待费',
  entertainment: '业务招待费',
  meeting: '会务费',
  marketing: '市场推广费',
  office: '办公用品费',
  training: '培训费',
  software: '软件服务费',
  communication: '通讯费',
  welfare: '福利费',
  other: '其他费用'
}

const SLOT_LABELS = {
  expense_type: '费用场景',
  amount: '申请金额',
  time_range: '业务时间',
  location: '业务地点',
  reason: '申请事由',
  days: '天数',
  transport_mode: '出行方式',
  attachments: '附件说明',
  customer_name: '客户名称',
  participants: '参与人员'
}

const PRE_APPROVAL_TYPES = new Set(['travel', 'meeting', 'office', 'training'])
const ATTACHMENT_REQUIRED_TYPES = new Set(['meeting', 'training'])
const PLACEHOLDER_VALUES = new Set(['', '待补充', '暂无', '无', '未知'])
const PROMPT_FIELD_LABELS = [
  '发生时间',
  '业务发生时间',
  '申请时间',
  '时间',
  '地点',
  '业务地点',
  '发生地点',
  '事由',
  '申请事由',
  '出差事由',
  '原因',
  '用途',
  '天数',
  '出差天数',
  '申请天数',
  '出行方式',
  '交通方式',
  '交通工具',
  '用户预估费用',
  '预估费用',
  '预计总费用',
  '预计费用',
  '预计金额',
  '申请金额',
  '预算',
  '金额'
]

export const APPLICATION_EXAMPLES = [
  '申请下周去北京做客户现场验收，预计费用18000元',
  '申请上海产品发布会会务费32000元，需要场地和物料',
  '申请部门集中采购办公用品4800元，用于新员工入职'
]

export function buildExpenseApplicationOntologyContext(currentUser = {}) {
  return {
    document_type: 'expense_application',
    application_stage: 'pre_approval',
    conversation_scenario: 'expense',
    entry_source: 'documents_application',
    role_codes: Array.isArray(currentUser.roleCodes) ? currentUser.roleCodes : [],
    is_admin: Boolean(currentUser.isAdmin),
    name: currentUser.name || '',
    role: currentUser.role || '',
    department: currentUser.department || currentUser.departmentName || '',
    department_name: currentUser.department || currentUser.departmentName || '',
    position: currentUser.position || '',
    grade: currentUser.grade || '',
    manager_name: currentUser.managerName || currentUser.manager_name || '',
    employee_no: currentUser.employeeNo || currentUser.employee_no || ''
  }
}

export function resolveEntity(ontology, type) {
  const entities = Array.isArray(ontology?.entities) ? ontology.entities : []
  return entities.find((item) => item?.type === type) || null
}

export function resolveConstraint(ontology, field) {
  const constraints = Array.isArray(ontology?.constraints) ? ontology.constraints : []
  return constraints.find((item) => item?.field === field) || null
}

export function resolveExpenseTypeCode(ontology) {
  const entity = resolveEntity(ontology, 'expense_type')
  return String(entity?.normalized_value || entity?.value || 'other').trim() || 'other'
}

function looksLikeStructuredTravelApplication(prompt) {
  const text = String(prompt || '')
  return /(?:发生时间|业务发生时间|申请时间|时间)\s*[:：]/.test(text)
    && /(?:地点|业务地点|发生地点|目的地)\s*[:：]/.test(text)
    && /(?:天数|出差天数|申请天数)\s*[:：]?\s*(?:\d+|[一二两三四五六七八九十]{1,3})\s*天/.test(text)
}

function resolveApplicationExpenseTypeCode(ontology, prompt) {
  const code = resolveExpenseTypeCode(ontology)
  if (code !== 'other') return code
  return looksLikeStructuredTravelApplication(prompt) ? 'travel' : code
}

export function resolveExpenseTypeLabel(code) {
  return EXPENSE_TYPE_LABELS[String(code || '').trim()] || EXPENSE_TYPE_LABELS.other
}

export function resolveApplicationAmount(ontology) {
  const amountEntity = resolveEntity(ontology, 'amount')
  const amountConstraint = resolveConstraint(ontology, 'amount')
  const rawValue = amountEntity?.normalized_value || amountEntity?.value || amountConstraint?.value || ''
  const numericValue = Number(String(rawValue).replace(/[^\d.]/g, ''))
  return {
    raw: String(rawValue || '').trim(),
    value: Number.isFinite(numericValue) ? numericValue : 0
  }
}

function resolveApplicationTypedAmount(ontology, type) {
  const entity = resolveEntity(ontology, type)
  const rawValue = entity?.normalized_value || entity?.value || ''
  const numericValue = Number(String(rawValue).replace(/[^\d.]/g, ''))
  return Number.isFinite(numericValue) && numericValue > 0 ? numericValue : 0
}

export function resolveTimeRangeText(ontology) {
  const range = ontology?.time_range || {}
  if (range.start_date && range.end_date) {
    return range.start_date === range.end_date
      ? range.start_date
      : `${range.start_date} 至 ${range.end_date}`
  }
  return String(range.raw || '').trim()
}

function parseApplicationDate(value) {
  const normalized = String(value || '')
    .trim()
    .replace(/日$/, '')
    .replace(/年|月|\//g, '-')
    .replace(/\./g, '-')
  const match = normalized.match(/^(20\d{2})-(\d{1,2})-(\d{1,2})$/)
  if (!match) return null
  const [, year, month, day] = match
  const date = new Date(Date.UTC(Number(year), Number(month) - 1, Number(day)))
  if (Number.isNaN(date.getTime())) return null
  return date
}

function formatApplicationDate(date) {
  return date.toISOString().slice(0, 10)
}

function parseChineseNumber(value) {
  const digits = {
    一: 1,
    二: 2,
    两: 2,
    三: 3,
    四: 4,
    五: 5,
    六: 6,
    七: 7,
    八: 8,
    九: 9
  }
  const text = String(value || '').trim()
  if (!text) return 0
  if (text === '十') return 10
  if (text.includes('十')) {
    const [left, right] = text.split('十')
    const tens = left ? digits[left] || 0 : 1
    const ones = right ? digits[right] || 0 : 0
    return tens * 10 + ones
  }
  return digits[text] || 0
}

export function resolvePromptDays(prompt) {
  const labeled = resolvePromptField(prompt, ['天数', '出差天数', '申请天数'])
  const source = labeled || String(prompt || '')
  const match = source.match(/(?<days>\d+|[一二两三四五六七八九十]{1,3})\s*天/)
  if (!match?.groups?.days) return 0
  if (/^\d+$/.test(match.groups.days)) return Number(match.groups.days)
  return parseChineseNumber(match.groups.days)
}

export function expandApplicationTimeWithDays(timeText, days = 0) {
  const normalizedTime = String(timeText || '').trim()
  const dayCount = Number(days || 0)
  if (!normalizedTime || !dayCount) return normalizedTime
  if (/\s*(至|到|~|--|—)\s*/.test(normalizedTime)) return normalizedTime

  const match = normalizedTime.match(/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/)
  const startDate = parseApplicationDate(match?.[0] || '')
  if (!startDate) return normalizedTime

  const endDate = new Date(startDate.getTime())
  endDate.setUTCDate(endDate.getUTCDate() + Math.max(dayCount - 1, 0))
  const startText = formatApplicationDate(startDate)
  const endText = formatApplicationDate(endDate)
  return startText === endText ? startText : `${startText} 至 ${endText}`
}

function normalizeApplicationTimeCandidate(value) {
  const text = String(value || '').trim().replace(/^[，,、。；;\s]+/, '')
  if (!text) return ''
  if (/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/.test(text)) return text
  if (/今天|明天|后天|本周|下周|上周|本月|下月|月底|月初/.test(text)) return text
  return ''
}

export function resolveApplicationTimeRange(ontology, prompt) {
  const range = ontology?.time_range || {}
  const baseTime = normalizeApplicationTimeCandidate(resolveTimeRangeText(ontology))
    || normalizeApplicationTimeCandidate(resolvePromptField(prompt, ['发生时间', '业务发生时间', '申请时间', '时间']))
  if (range.start_date && range.end_date && range.start_date !== range.end_date) {
    return `${range.start_date} 至 ${range.end_date}`
  }
  return expandApplicationTimeWithDays(baseTime, resolvePromptDays(prompt)) || baseTime
}

function escapeRegExp(value) {
  return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

export function resolvePromptField(prompt, labels = []) {
  const text = String(prompt || '').trim()
  if (!text) return ''
  const labelSet = new Set(labels.map((item) => String(item || '').trim()).filter(Boolean))
  for (const line of text.split(/\r?\n/)) {
    const match = line.match(/^\s*([^:：\s]+)\s*[:：]\s*(.+?)\s*$/)
    if (match && labelSet.has(match[1].trim())) {
      return match[2].trim()
    }
  }

  const labelPattern = labels.map(escapeRegExp).join('|')
  const nextLabelPattern = PROMPT_FIELD_LABELS.map(escapeRegExp).join('|')
  if (!labelPattern) return ''
  const match = text.match(
    new RegExp(`(?:${labelPattern})\\s*[:：]\\s*([\\s\\S]*?)(?=\\s*(?:${nextLabelPattern})\\s*[:：]|$)`)
  )
  return match ? match[1].trim().replace(/[，。；;]$/, '') : ''
}

function normalizeApplicationTransportMode(value) {
  const text = String(value || '').trim()
  if (/飞机|机票|航班/.test(text)) return '飞机'
  if (/轮船|船票|客轮|渡轮|邮轮/.test(text)) return '轮船'
  if (/火车|高铁|动车|铁路|列车/.test(text)) return '火车'
  return text
}

function cleanupApplicationReasonCandidate(value, location = '') {
  let text = String(value || '').trim()
  if (!text) return ''

  text = text
    .replace(/(?:请直接生成申请单核对结果|信息足够时生成申请单|但在入库或提交审批前仍需让我确认|请直接生成报销核对结果|需要创建草稿、绑定附件或提交审批前仍需让我确认)[\s\S]*$/u, '')
    .replace(/^(?:类型|申请类型|费用类型|报销类型|发生时间|业务发生时间|申请时间|时间|地点|业务地点|发生地点|天数|出差天数|申请天数|出行方式|交通方式|交通工具|用户预估费用|预估费用|预计总费用|预计费用|预计金额|申请金额|预算|金额)\s*[:：]\s*/u, '')
    .replace(/20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?/gu, '')
    .replace(/(?:出差|申请)?(?:\d+|[一二两三四五六七八九十]{1,3})\s*天/gu, '')
    .replace(/(?:¥|￥)?\s*\d+(?:\.\d+)?\s*(?:元|块|万元|人民币)?/gu, '')
    .replace(/(?:高铁|动车|火车|铁路|列车|飞机|机票|航班|轮船|船票|客轮|渡轮|邮轮)/gu, '')
    .replace(/[，,、。；;]+/g, '，')
    .replace(/^\s*(申请|费用申请|业务|本次|去|到|前往|赴)\s*/u, '')
    .replace(/^[，\s]+|[，\s]+$/g, '')
    .trim()

  const normalizedLocation = String(location || '').trim()
  if (normalizedLocation) {
    const escapedLocation = escapeRegExp(normalizedLocation)
    text = text
      .replace(new RegExp(`^${escapedLocation}(?:出差)?(?:，|,|、)?`, 'u'), '')
      .replace(new RegExp(`^(?:去|到|前往|赴)${escapedLocation}(?:出差)?(?:，|,|、)?`, 'u'), '')
      .trim()
  }

  if (!text) return ''
  if (isInvalidApplicationReason(text)) return ''
  if (/^20\d{2}[-/.年]\d{1,2}[-/.月]\d{1,2}日?$/.test(text)) return ''
  if (/^(?:\d+|[一二两三四五六七八九十]{1,3})\s*天$/.test(text)) return ''
  if (/^[\u4e00-\u9fa5]{1,8}$/.test(text) && !/服务|支撑|支持|部署|实施|验收|拜访|对接|沟通|培训|会议|采购|安装|维护|上线|调试|项目/.test(text)) {
    return ''
  }

  return text
}

function resolveApplicationLocationText(ontology, prompt) {
  const locationEntity = resolveEntity(ontology, 'location')
  return locationEntity?.normalized_value
    || locationEntity?.value
    || resolvePromptField(prompt, ['地点', '业务地点', '发生地点', '目的地'])
    || ''
}

export function resolveApplicationReason(prompt, ontology = null) {
  const location = resolveApplicationLocationText(ontology, prompt)
  const reasonEntity = resolveEntity(ontology, 'reason') || resolveEntity(ontology, 'business_reason')
  const entityReason = String(reasonEntity?.normalized_value || reasonEntity?.value || '').trim()
  if (entityReason) {
    const cleanedEntityReason = cleanupApplicationReasonCandidate(entityReason, location)
    if (cleanedEntityReason && !isInvalidApplicationReason(cleanedEntityReason)) {
      return cleanedEntityReason
    }
  }

  const labeled = resolvePromptField(prompt, ['事由', '申请事由', '出差事由', '原因', '用途'])
  if (labeled) {
    const cleanedLabeledReason = cleanupApplicationReasonCandidate(labeled, location)
    if (cleanedLabeledReason && !isInvalidApplicationReason(cleanedLabeledReason)) {
      return cleanedLabeledReason
    }
  }

  const candidates = String(prompt || '')
    .split(/[\n，。；;]+/u)
    .map((item) => cleanupApplicationReasonCandidate(item, location))
    .filter((item) => item && !isSystemGeneratedApplicationReason(item) && !isInvalidApplicationReason(item))
  const businessCandidate = candidates.find((item) => /服务|支撑|支持|部署|实施|验收|拜访|对接|沟通|培训|会议|采购|安装|维护|上线|调试|项目/.test(item))
  return businessCandidate || candidates.sort((left, right) => right.length - left.length)[0] || ''
}

function isInvalidApplicationReason(value = '') {
  const compact = String(value || '').replace(/\s+/g, '')
  if (!compact) return true
  if (/^(?:类型|申请类型|费用类型|报销类型)[:：]?/.test(compact)) return true
  if (/^(?:差旅费用申请|交通费用申请|住宿费用申请|费用申请|差旅费|交通费|住宿费)$/.test(compact)) return true
  return false
}

function isSystemGeneratedApplicationReason(value = '') {
  const compact = String(value || '').replace(/\s+/g, '')
  return compact.startsWith('小财管家继续执行')
    || compact.startsWith('处理要求')
    || compact.startsWith('已识别信息')
    || compact.startsWith('用户已补充')
}

function resolveApplicationTransportMode(ontology, prompt) {
  const transportEntity = resolveEntity(ontology, 'transport_mode')
    || resolveEntity(ontology, 'transport')
  const fromEntity = normalizeApplicationTransportMode(
    transportEntity?.normalized_value || transportEntity?.value || ''
  )
  if (fromEntity) return fromEntity

  const labeled = resolvePromptField(prompt, ['出行方式', '交通方式', '交通工具'])
  const fromLabel = normalizeApplicationTransportMode(labeled)
  if (fromLabel) return fromLabel

  const text = String(prompt || '')
  if (/飞机|机票|航班/.test(text)) return '飞机'
  if (/轮船|船票|客轮|渡轮|邮轮/.test(text)) return '轮船'
  if (/火车|高铁|动车|铁路|列车/.test(text)) return '火车'
  return ''
}

export function resolveAttachmentPolicy(expenseTypeCode, amount = 0) {
  const code = String(expenseTypeCode || '').trim()
  if (ATTACHMENT_REQUIRED_TYPES.has(code)) {
    return {
      level: 'required',
      label: '必须提交',
      description: code === 'meeting'
        ? '需补充会议通知、议程、参会范围或预算说明。'
        : '需补充培训通知、课程说明、报价或审批依据。'
    }
  }
  if (code === 'office' && amount >= 5000) {
    return {
      level: 'required',
      label: '必须提交',
      description: '办公采购金额较高，需补充采购清单、报价或预算说明。'
    }
  }
  if (code === 'travel') {
    return {
      level: 'optional',
      label: '说明可选',
      description: '可先提交出差目的、时间和预算；行程或邀请材料可作为补充说明。'
    }
  }
  return {
    level: 'none',
    label: '无需附件',
    description: '当前申请事项可先不提交附件，后续报销阶段再按票据要求补充。'
  }
}

export function buildApplicationFieldsFromOntology(ontology, prompt, currentUser = {}) {
  const expenseTypeCode = resolveApplicationExpenseTypeCode(ontology, prompt)
  const amount = resolveApplicationAmount(ontology)
  const documentTypeEntity = resolveEntity(ontology, 'document_type')
  const workflowStageEntity = resolveEntity(ontology, 'workflow_stage')
  const attachmentPolicy = resolveAttachmentPolicy(expenseTypeCode, amount.value)
  const timeRange = resolveApplicationTimeRange(ontology, prompt)
    || '待补充'
  const location = resolveApplicationLocationText(ontology, prompt)
    || '待补充'
  const reason = resolveApplicationReason(prompt, ontology) || '待补充'
  const days = resolvePromptDays(prompt)
  const transportMode = resolveApplicationTransportMode(ontology, prompt)
  const transportEstimatedAmount = resolveApplicationTypedAmount(ontology, 'transport_estimated_amount')
  const trainEstimatedAmount = resolveApplicationTypedAmount(ontology, 'train_estimated_amount')
  const flightEstimatedAmount = resolveApplicationTypedAmount(ontology, 'flight_estimated_amount')
  const hotelAmount = resolveApplicationTypedAmount(ontology, 'hotel_amount')
  const allowanceAmount = resolveApplicationTypedAmount(ontology, 'allowance_amount')
  const policyTotalAmount = resolveApplicationTypedAmount(ontology, 'policy_total_amount')
  const reimbursementAmount = resolveApplicationTypedAmount(ontology, 'reimbursement_amount')

  const fields = {
    documentType: documentTypeEntity?.normalized_value || 'expense_application',
    documentTypeLabel: documentTypeEntity?.value || '费用申请',
    workflowStage: workflowStageEntity?.normalized_value || 'pre_approval',
    workflowStageLabel: workflowStageEntity?.value || '前置申请',
    expenseTypeCode,
    expenseTypeLabel: resolveExpenseTypeLabel(expenseTypeCode),
    amount: amount.value,
    amountDisplay: amount.value ? `¥${amount.value.toLocaleString('zh-CN')}` : '待补充',
    transportEstimatedAmount,
    trainEstimatedAmount,
    flightEstimatedAmount,
    hotelAmount,
    allowanceAmount,
    policyTotalAmount,
    reimbursementAmount,
    timeRange,
    location,
    reason,
    days: days ? `${days}天` : '',
    transportMode,
    applicant: currentUser.name || currentUser.username || '当前用户',
    department: currentUser.department || currentUser.departmentName || '待补充',
    position: currentUser.position || currentUser.employeePosition || '待补充',
    managerName: currentUser.managerName || currentUser.manager_name || '待补充',
    preApprovalRequired: PRE_APPROVAL_TYPES.has(expenseTypeCode),
    attachmentPolicy
  }

  return {
    ...fields,
    missingSlots: normalizeMissingSlots(ontology?.missing_slots || [], fields)
  }
}

function hasProvidedValue(value) {
  const normalized = String(value || '').trim()
  return !PLACEHOLDER_VALUES.has(normalized)
}

function isSlotAlreadyResolved(slot, fields = {}) {
  const key = String(slot || '').trim()
  if (key === 'reason') return hasProvidedValue(fields.reason)
  if (key === 'time_range' || key === 'time') return hasProvidedValue(fields.timeRange)
  if (key === 'location') return hasProvidedValue(fields.location)
  if (key === 'amount') return Number(fields.amount || 0) > 0
  if (key === 'transport_mode') return hasProvidedValue(fields.transportMode)
  return false
}

export function normalizeMissingSlots(slots = [], fields = {}) {
  const normalized = Array.isArray(slots) ? slots : []
  return normalized.map((item) => ({
    key: String(item || '').trim(),
    label: SLOT_LABELS[String(item || '').trim()] || String(item || '').trim()
  })).filter((item) => item.key && !isSlotAlreadyResolved(item.key, fields))
}
