import { isApplicationDocumentNo } from '../../utils/documentClassification.js'

const REQUIRED_APPLICATION_EXPENSE_TYPES = new Set(['travel', 'meal'])

const APPLICATION_TYPE_ALIASES = {
  travel: new Set(['travel', 'travel_application']),
  meal: new Set([
    'meal',
    'entertainment',
    'meal_application',
    'entertainment_application',
    'business_entertainment_application',
    'hospitality_application'
  ])
}

const GENERIC_APPLICATION_TYPES = new Set(['application', 'expense_application'])
const APPROVED_APPLICATION_STATUSES = new Set(['approved', 'completed'])
const APPROVED_APPLICATION_APPROVAL_KEYS = new Set(['completed'])
const BLOCKED_APPLICATION_LINK_STATUSES = new Set(['draft', 'returned', 'rejected', 'archived', 'cancelled', 'canceled', 'deleted'])
const INACTIVE_REIMBURSEMENT_LINK_STATUSES = new Set(['cancelled', 'canceled', 'deleted'])

const STATUS_LABELS = {
  submitted: '审批中',
  approved: '已审批',
  completed: '已完成',
  archived: '已归档',
  pending_payment: '待付款',
  paid: '已付款'
}

const EXPENSE_TYPE_LABELS = {
  travel: '差旅费',
  meal: '业务招待费'
}

function normalizeText(value) {
  return String(value || '').trim()
}

function normalizeLower(value) {
  return normalizeText(value).toLowerCase()
}

function uniqueValues(values) {
  return Array.from(new Set((Array.isArray(values) ? values : []).map(normalizeText).filter(Boolean)))
}

function expandIdentityValues(values) {
  const expanded = []
  ;(Array.isArray(values) ? values : []).forEach((value) => {
    const normalized = normalizeText(value)
    if (!normalized) {
      return
    }
    expanded.push(normalized)
    const atIndex = normalized.indexOf('@')
    if (atIndex > 0) {
      expanded.push(normalized.slice(0, atIndex))
    }
  })
  return uniqueValues(expanded)
}

function normalizeClaimNo(claim) {
  return normalizeText(claim?.claim_no || claim?.claimNo).toUpperCase()
}

function normalizeExpenseType(claim) {
  return normalizeLower(claim?.expense_type || claim?.expenseType || claim?.type_code || claim?.typeCode)
}

function normalizeApplicationStatus(claim) {
  return normalizeLower(claim?.status || claim?.state || claim?.approval_status || claim?.approvalStatus)
}

function normalizeApprovalKey(claim) {
  return normalizeLower(claim?.approvalKey || claim?.approval_key)
}

function normalizeDocumentType(claim) {
  return normalizeLower(
    claim?.document_type_code
      || claim?.documentTypeCode
      || claim?.document_type
      || claim?.documentType
  )
}

function normalizeApplicationDate(claim) {
  return normalizeText(
    claim?.submitted_at
      || claim?.submittedAt
      || claim?.created_at
      || claim?.createdAt
      || claim?.occurred_at
      || claim?.occurredAt
  )
}

function normalizeApplicationDateText(value) {
  const text = normalizeText(value)
  if (!text) {
    return ''
  }
  const matched = text.match(/^(\d{4}-\d{2}-\d{2})/)
  return matched?.[1] || text
}

function normalizeApplicationBusinessTime(claim) {
  const detail = resolveApplicationDetailPayload(claim)
  const start = normalizeApplicationDateText(
    detail.start_date
      || detail.startDate
      || detail.departure_date
      || detail.departureDate
      || claim?.start_date
      || claim?.startDate
      || claim?.begin_date
      || claim?.beginDate
  )
  const end = normalizeApplicationDateText(
    detail.end_date
      || detail.endDate
      || detail.return_date
      || detail.returnDate
      || claim?.end_date
      || claim?.endDate
      || claim?.finish_date
      || claim?.finishDate
  )
  if (start && end && start !== end) {
    return `${start} 至 ${end}`
  }
  return normalizeApplicationDateText(
    start
      || detail.application_business_time
      || detail.applicationBusinessTime
      || detail.business_time
      || detail.businessTime
      || detail.time_range
      || detail.timeRange
      || claim?.business_time
      || claim?.businessTime
      || claim?.time_range
      || claim?.timeRange
      || claim?.occurred_at
      || claim?.occurredAt
      || claim?.occurred_date
      || claim?.occurredDate
  )
}

function resolveApplicationDetailPayload(claim) {
  const flags = Array.isArray(claim?.risk_flags_json)
    ? claim.risk_flags_json
    : Array.isArray(claim?.riskFlags)
      ? claim.riskFlags
      : []
  const detailFlag = flags.find((flag) => (
    flag &&
    typeof flag === 'object' &&
    normalizeLower(flag.source) === 'application_detail'
  ))
  const detail = detailFlag?.application_detail || detailFlag?.applicationDetail || {}
  return detail && typeof detail === 'object' ? detail : {}
}

function resolveRiskFlags(claim) {
  const flags = claim?.risk_flags_json || claim?.riskFlags || []
  return Array.isArray(flags) ? flags : []
}

function createReferenceIndex() {
  return {
    ids: new Set(),
    claimNos: new Set()
  }
}

function addApplicationReference(index, idValue, claimNoValue) {
  const id = normalizeText(idValue)
  if (id) {
    index.ids.add(id)
  }
  const claimNo = normalizeText(claimNoValue).toUpperCase()
  if (claimNo) {
    index.claimNos.add(claimNo)
  }
}

function addApplicationReferencesFromPayload(index, payload) {
  if (!payload || typeof payload !== 'object') {
    return
  }

  addApplicationReference(
    index,
    payload.application_claim_id || payload.applicationClaimId || payload.id,
    payload.application_claim_no || payload.applicationClaimNo || payload.claim_no || payload.claimNo
  )
}

function collectLinkedApplicationReferences(claim) {
  const index = createReferenceIndex()
  addApplicationReferencesFromPayload(index, claim?.relatedApplication)
  addApplicationReferencesFromPayload(index, claim?.related_application)
  resolveRiskFlags(claim).forEach((flag) => {
    if (!flag || typeof flag !== 'object') {
      return
    }
    addApplicationReferencesFromPayload(index, flag)
    addApplicationReferencesFromPayload(index, flag.application_detail || flag.applicationDetail)
    addApplicationReferencesFromPayload(index, flag.review_form_values || flag.reviewFormValues)
    addApplicationReferencesFromPayload(index, flag.expense_scene_selection || flag.expenseSceneSelection)
  })
  return index
}

function hasAnyApplicationReference(index) {
  return Boolean(index?.ids?.size || index?.claimNos?.size)
}

export function buildLinkedApplicationReferenceIndex(claims) {
  const index = createReferenceIndex()
  ;(Array.isArray(claims) ? claims : []).forEach((claim) => {
    if (isExpenseApplicationClaim(claim)) {
      return
    }

    const status = normalizeApplicationStatus(claim)
    if (INACTIVE_REIMBURSEMENT_LINK_STATUSES.has(status)) {
      return
    }

    const claimReferences = collectLinkedApplicationReferences(claim)
    claimReferences.ids.forEach((id) => index.ids.add(id))
    claimReferences.claimNos.forEach((claimNo) => index.claimNos.add(claimNo))
  })
  return index
}

function isApplicationAlreadyLinked(claim, linkedApplicationReferences) {
  if (!linkedApplicationReferences) {
    return false
  }

  const ownReferences = createReferenceIndex()
  addApplicationReference(
    ownReferences,
    claim?.id || claim?.claim_id || claim?.claimId,
    claim?.claim_no || claim?.claimNo
  )
  if (!hasAnyApplicationReference(ownReferences)) {
    return false
  }

  return Array.from(ownReferences.ids).some((id) => linkedApplicationReferences.ids.has(id))
    || Array.from(ownReferences.claimNos).some((claimNo) => linkedApplicationReferences.claimNos.has(claimNo))
}

function toTimestamp(value) {
  const date = new Date(value)
  return Number.isNaN(date.getTime()) ? 0 : date.getTime()
}

function formatAmount(value) {
  const numberValue = Number(String(value ?? '').replace(/[^\d.-]/g, ''))
  if (!Number.isFinite(numberValue) || numberValue <= 0) {
    return ''
  }
  return `¥${new Intl.NumberFormat('zh-CN', {
    minimumFractionDigits: Number.isInteger(numberValue) ? 0 : 2,
    maximumFractionDigits: 2
  }).format(numberValue)}`
}

function includesAny(text, keywords) {
  const normalized = normalizeText(text)
  return keywords.some((keyword) => normalized.includes(keyword))
}

function buildApplicationKeywordText(claim) {
  return [
    claim?.reason,
    claim?.business_reason,
    claim?.title,
    claim?.summary,
    claim?.description,
    claim?.location,
    claim?.business_location,
    claim?.expense_type_label,
    claim?.expenseTypeLabel
  ].map(normalizeText).filter(Boolean).join(' ')
}

function matchesGenericApplicationByText(claim, expenseType) {
  const haystack = buildApplicationKeywordText(claim)
  if (expenseType === 'travel') {
    return includesAny(haystack, ['差旅', '出差', '住宿', '交通', '行程'])
  }
  if (expenseType === 'meal') {
    return includesAny(haystack, ['招待', '客户', '接待', '宴请', '用餐', '餐饮'])
  }
  return false
}

export function requiresApplicationBeforeReimbursement(expenseType) {
  return REQUIRED_APPLICATION_EXPENSE_TYPES.has(normalizeLower(expenseType))
}

export function getRequiredApplicationExpenseLabel(expenseType) {
  return EXPENSE_TYPE_LABELS[normalizeLower(expenseType)] || '报销'
}

export function resolveRequiredApplicationReimbursementType(application = {}) {
  const expenseType = normalizeLower(
    application.application_expense_type
    || application.expense_type
    || application.expenseType
    || application.type_code
    || application.typeCode
  )
  const source = {
    expense_type: expenseType,
    reason: application.application_reason || application.reason,
    title: application.application_reason || application.reason,
    description: application.application_reason || application.reason,
    location: application.application_location || application.location
  }

  if (APPLICATION_TYPE_ALIASES.travel.has(expenseType)) {
    return {
      expenseType: 'travel',
      expenseTypeLabel: EXPENSE_TYPE_LABELS.travel
    }
  }

  if (APPLICATION_TYPE_ALIASES.meal.has(expenseType)) {
    return {
      expenseType: 'meal',
      expenseTypeLabel: EXPENSE_TYPE_LABELS.meal
    }
  }

  if (matchesGenericApplicationByText(source, 'meal')) {
    return {
      expenseType: 'meal',
      expenseTypeLabel: EXPENSE_TYPE_LABELS.meal
    }
  }

  if (matchesGenericApplicationByText(source, 'travel')) {
    return {
      expenseType: 'travel',
      expenseTypeLabel: EXPENSE_TYPE_LABELS.travel
    }
  }

  return {
    expenseType: REQUIRED_APPLICATION_EXPENSE_TYPES.has(expenseType) ? expenseType : 'travel',
    expenseTypeLabel: getRequiredApplicationExpenseLabel(
      REQUIRED_APPLICATION_EXPENSE_TYPES.has(expenseType) ? expenseType : 'travel'
    )
  }
}

export function isExpenseApplicationClaim(claim) {
  const documentType = normalizeDocumentType(claim)
  const expenseType = normalizeExpenseType(claim)
  const claimNo = normalizeClaimNo(claim)

  return documentType === 'application'
    || documentType === 'expense_application'
    || isApplicationDocumentNo(claimNo)
    || expenseType === 'application'
    || expenseType.endsWith('_application')
}

export function matchesRequiredApplicationExpenseType(claim, expenseType) {
  const normalizedExpenseType = normalizeLower(expenseType)
  const claimExpenseType = normalizeExpenseType(claim)
  const aliases = APPLICATION_TYPE_ALIASES[normalizedExpenseType] || new Set()

  if (aliases.has(claimExpenseType)) {
    return true
  }

  return GENERIC_APPLICATION_TYPES.has(claimExpenseType)
    && matchesGenericApplicationByText(claim, normalizedExpenseType)
}

export function isClaimOwnedByCurrentUser(claim, currentUser = {}) {
  const userIds = expandIdentityValues([
    currentUser.id,
    currentUser.employeeId,
    currentUser.employee_id,
    currentUser.employeeNo,
    currentUser.employee_no,
    currentUser.username,
    currentUser.email
  ])
  const claimIds = expandIdentityValues([
    claim?.employee_id,
    claim?.employeeId,
    claim?.employee_no,
    claim?.employeeNo,
    claim?.employee_email,
    claim?.employeeEmail,
    claim?.username,
    claim?.user_id,
    claim?.userId
  ])
  if (userIds.length && claimIds.length && claimIds.some((item) => userIds.includes(item))) {
    return true
  }

  const userNames = uniqueValues([
    currentUser.name,
    currentUser.user_name,
    currentUser.employeeName,
    currentUser.employee_name,
    currentUser.username
  ])
  const claimNames = uniqueValues([
    claim?.employee_name,
    claim?.employeeName,
    claim?.applicant,
    claim?.applicant_name,
    claim?.applicantName
  ])
  if (userNames.length && claimNames.length) {
    return claimNames.some((item) => userNames.includes(item))
  }

  return true
}

export function isUsableRequiredApplicationClaim(claim, linkedApplicationReferences = null) {
  const status = normalizeApplicationStatus(claim)
  const approvalKey = normalizeApprovalKey(claim)
  if (BLOCKED_APPLICATION_LINK_STATUSES.has(status)) {
    return false
  }
  return (APPROVED_APPLICATION_STATUSES.has(status) || APPROVED_APPLICATION_APPROVAL_KEYS.has(approvalKey))
    && !isApplicationAlreadyLinked(claim, linkedApplicationReferences)
}

export function normalizeRequiredApplicationCandidate(claim) {
  const detail = resolveApplicationDetailPayload(claim)
  const claimNo = normalizeText(claim?.claim_no || claim?.claimNo)
  const location = normalizeText(
    detail.location
      || detail.application_location
      || claim?.location
      || claim?.business_location
      || claim?.businessLocation
  )
  const amount = normalizeText(
    detail.amount
      || detail.application_amount
      || claim?.amount
      || claim?.budget_amount
      || claim?.budgetAmount
  )
  const amountText = formatAmount(amount)
  const status = normalizeApplicationStatus(claim)

  return {
    id: normalizeText(claim?.id || claim?.claim_id || claim?.claimId),
    claim_no: claimNo,
    expense_type: normalizeExpenseType(claim),
    reason: normalizeText(detail.reason || detail.application_reason || claim?.reason || claim?.business_reason || claim?.description || claim?.title),
    location,
    amount,
    amount_label: amountText,
    business_time: normalizeText(
      detail.application_business_time
        || detail.applicationBusinessTime
        || detail.business_time
        || detail.businessTime
        || detail.time_range
        || detail.timeRange
        || detail.time
        || detail.application_time
    ) || normalizeApplicationBusinessTime(claim),
    days: normalizeText(detail.days || detail.application_days),
    transport_mode: normalizeText(detail.transport_mode || detail.application_transport_mode),
    lodging_daily_cap: normalizeText(detail.lodging_daily_cap || detail.application_lodging_daily_cap),
    subsidy_daily_cap: normalizeText(detail.subsidy_daily_cap || detail.application_subsidy_daily_cap),
    transport_policy: normalizeText(detail.transport_policy || detail.application_transport_policy),
    policy_estimate: normalizeText(detail.policy_estimate || detail.application_policy_estimate),
    rule_name: normalizeText(detail.rule_name || detail.application_rule_name),
    rule_version: normalizeText(detail.rule_version || detail.application_rule_version),
    status,
    status_label: STATUS_LABELS[status] || normalizeText(claim?.approval_stage || claim?.approvalStage || status),
    application_date: normalizeApplicationDate(claim)
  }
}

export function filterRequiredApplicationCandidates(claimsPayload, expenseType, currentUser = {}) {
  const claims = Array.isArray(claimsPayload)
    ? claimsPayload
    : Array.isArray(claimsPayload?.items)
      ? claimsPayload.items
      : Array.isArray(claimsPayload?.claims)
        ? claimsPayload.claims
        : []

  const linkedApplicationReferences = buildLinkedApplicationReferenceIndex(claims)

  return claims
    .filter((claim) => (
      isExpenseApplicationClaim(claim)
      && isUsableRequiredApplicationClaim(claim, linkedApplicationReferences)
      && isClaimOwnedByCurrentUser(claim, currentUser)
      && matchesRequiredApplicationExpenseType(claim, expenseType)
    ))
    .map(normalizeRequiredApplicationCandidate)
    .sort((left, right) => toTimestamp(right.application_date) - toTimestamp(left.application_date))
}

export function buildRequiredApplicationActions(applications, actionType) {
  return (Array.isArray(applications) ? applications : []).map((application) => {
    const claimNo = normalizeText(application.claim_no) || '未编号申请单'
    const description = [
      application.status_label,
      application.business_time && `时间：${application.business_time}`,
      application.location && `地点：${application.location}`,
      application.amount_label && `预算：${application.amount_label}`,
      application.reason && `事由：${application.reason}`
    ].filter(Boolean).join(' · ')

    return {
      label: claimNo,
      description,
      icon: 'mdi mdi-file-link-outline',
      action_type: actionType,
      payload: {
        application_claim_id: application.id,
        application_claim_no: application.claim_no,
        application_expense_type: application.expense_type,
        application_reason: application.reason,
        application_location: application.location,
        application_amount: application.amount,
        application_amount_label: application.amount_label,
        application_business_time: application.business_time,
        application_days: application.days,
        application_transport_mode: application.transport_mode,
        application_lodging_daily_cap: application.lodging_daily_cap,
        application_subsidy_daily_cap: application.subsidy_daily_cap,
        application_transport_policy: application.transport_policy,
        application_policy_estimate: application.policy_estimate,
        application_rule_name: application.rule_name,
        application_rule_version: application.rule_version,
        application_status: application.status,
        application_status_label: application.status_label,
        application_date: application.application_date
      }
    }
  })
}

export function buildRequiredApplicationSelectionText(expenseType, applications) {
  const label = getRequiredApplicationExpenseLabel(expenseType)
  return [
    `发起“${label}”报销前，需要先关联对应的申请单。`,
    '',
    `我查到 ${applications.length} 个可关联申请单，请先选择其中一个。`,
    '选择后，我会继续向您收集本次报销依据。'
  ].join('\n')
}

export function buildRequiredApplicationMissingText(expenseType) {
  const label = getRequiredApplicationExpenseLabel(expenseType)
  return [
    `发起“${label}”报销前，需要先关联对应的申请单。`,
    '',
    `我没有查到您名下可关联的“${label}”申请单，所以当前不能继续这类报销流程。`,
    '请先切换到申请助手发起相关申请；申请单存在后，再回到报销助手继续。'
  ].join('\n')
}
