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

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

function cardLikeText(card = {}) {
  if (!card || typeof card !== 'object') {
    return normalizeText(card)
  }
  return [
    card.title,
    card.label,
    card.name,
    card.summary,
    card.message,
    card.reason,
    card.suggestion,
    card.ruleName,
    card.rule_name,
    card.ruleCode,
    card.rule_code,
    card.evidence?.summary,
    card.evidence?.reason
  ].map((value) => normalizeText(value)).filter(Boolean).join(' ')
}

function isRouteLevelRiskText(value) {
  const text = normalizeText(value)
  return /行程|多城市|目的地|票据城市|差旅地点|中转|改签|异地/.test(text)
}

function isTravelRouteExpenseItem(item = {}) {
  const text = [
    item.name,
    item.category,
    item.desc,
    item.detail,
    item.itemType,
    item.item_type
  ].map((value) => normalizeText(value)).join(' ')
  if (/补贴|系统自动计算/.test(text)) {
    return false
  }
  return /火车|高铁|机票|航班|交通票|出发|返回|中转|起始地|目的地|[-－—~至]/.test(text)
}

const GENERIC_ROUTE_CITY_TOKENS = new Set([
  '起始地',
  '目的地',
  '出发地',
  '返回地',
  '中转地',
  '城市',
  '地点'
])

function normalizeRouteCityToken(value) {
  const text = normalizeText(value).replace(/[，。；、]/g, '').replace(/市$/, '')
  if (!text || GENERIC_ROUTE_CITY_TOKENS.has(text)) {
    return ''
  }
  return text
}

function extractRouteCityPairFromText(value) {
  const text = normalizeText(value)
  if (!text) {
    return null
  }
  const match = text.match(/([\u4e00-\u9fa5]{2,8})\s*(?:市)?\s*[-－—–~～至到]\s*([\u4e00-\u9fa5]{2,8})\s*(?:市)?/)
  if (!match) {
    return null
  }
  const origin = normalizeRouteCityToken(match[1])
  const destination = normalizeRouteCityToken(match[2])
  if (!origin || !destination || origin === destination) {
    return null
  }
  return { origin, destination }
}

function resolveTravelRouteInfo(item = {}) {
  if (!normalizeId(item?.id) || !isTravelRouteExpenseItem(item)) {
    return null
  }
  const pair = [
    item.desc,
    item.itemReason,
    item.item_reason,
    item.itemLocation,
    item.item_location,
    item.detail
  ]
    .map((value) => extractRouteCityPairFromText(value))
    .find(Boolean)
  if (!pair) {
    return null
  }
  return {
    id: normalizeId(item.id),
    cities: [pair.origin, pair.destination],
    origin: pair.origin,
    destination: pair.destination
  }
}

function uniqueTextList(values) {
  return (Array.isArray(values) ? values : [])
    .map((value) => normalizeText(value))
    .filter(Boolean)
    .filter((value, index, list) => list.indexOf(value) === index)
}

function resolveRoundTripBaseCities(routeInfos) {
  if (!Array.isArray(routeInfos) || routeInfos.length < 2) {
    return new Set()
  }
  const first = routeInfos[0]
  const last = routeInfos[routeInfos.length - 1]
  if (
    first?.origin
    && first?.destination
    && last?.origin
    && last?.destination
    && first.origin === last.destination
    && first.destination === last.origin
  ) {
    return new Set([first.origin, first.destination])
  }
  return new Set()
}

function resolveUnexpectedRouteCitiesForRisk(text, routeInfos) {
  const routeCities = uniqueTextList(routeInfos.flatMap((item) => item.cities))
  if (!routeCities.length) {
    return []
  }

  const baseCities = resolveRoundTripBaseCities(routeInfos)
  const mentionedCities = routeCities.filter((city) => text.includes(city))
  const mentionedExtraCities = mentionedCities.filter((city) => !baseCities.has(city))
  if (mentionedExtraCities.length) {
    return mentionedExtraCities
  }

  if (baseCities.size) {
    return routeCities.filter((city) => !baseCities.has(city))
  }

  return mentionedCities
}

function inferRouteRelatedItemIds(flag, risks, expenseItems) {
  const text = [
    cardLikeText(flag),
    ...uniqueTextList(risks)
  ].map((value) => normalizeText(value)).join(' ')
  if (!isRouteLevelRiskText(text)) {
    return []
  }
  const routeInfos = (Array.isArray(expenseItems) ? expenseItems : [])
    .map((item) => resolveTravelRouteInfo(item))
    .filter(Boolean)
  const unexpectedCities = resolveUnexpectedRouteCitiesForRisk(text, routeInfos)
  if (!unexpectedCities.length) {
    return []
  }
  return routeInfos
    .filter((item) => item.cities.some((city) => unexpectedCities.includes(city)))
    .map((item) => item.id)
}

export function resolveRouteRelatedItemIdsForRisk({
  flagItemIds = [],
  flag = {},
  risks = [],
  expenseItems = []
} = {}) {
  const normalizedFlagItemIds = (Array.isArray(flagItemIds) ? flagItemIds : [])
    .map((itemId) => normalizeId(itemId))
    .filter(Boolean)
  const inferredItemIds = inferRouteRelatedItemIds(flag, risks, expenseItems)
  if (!normalizedFlagItemIds.length) {
    return inferredItemIds
  }
  if (
    inferredItemIds.length
    && inferredItemIds.length < normalizedFlagItemIds.length
    && inferredItemIds.every((itemId) => normalizedFlagItemIds.includes(itemId))
  ) {
    return inferredItemIds
  }
  return normalizedFlagItemIds
}
