import {
  STANDARD_ADJUSTMENT_RISK_SOURCE,
  buildStandardAdjustmentMap
} from './travelRequestDetailExpenseModel.js'

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

function normalizeAmount(value) {
  const amount = Number(value)
  return Number.isFinite(amount) && amount > 0 ? amount : 0
}

function parseDayCount(value) {
  const match = String(value || '').match(/\d{1,3}/)
  if (!match) {
    return 0
  }
  const days = Number(match[0])
  return Number.isFinite(days) && days > 0 ? Math.max(1, Math.min(365, Math.floor(days))) : 0
}

export function buildCurrentStandardAdjustmentMap(request = {}, riskFlags = []) {
  return buildStandardAdjustmentMap({
    ...request,
    riskFlags,
    risk_flags_json: riskFlags
  })
}

export function resolveExpenseItemForRiskCard(card, expenseItems = []) {
  const itemIds = [
    card?.itemId,
    card?.item_id,
    ...(Array.isArray(card?.itemIds) ? card.itemIds : []),
    ...(Array.isArray(card?.item_ids) ? card.item_ids : [])
  ].map(normalizeText).filter(Boolean)
  const invoiceId = normalizeText(card?.invoiceId || card?.invoice_id)
  const itemIndex = Number(card?.itemIndex || card?.item_index || 0)

  return expenseItems.find((item) => itemIds.includes(normalizeText(item.id)))
    || expenseItems.find((item) => invoiceId && normalizeText(item.invoiceId) === invoiceId)
    || (itemIndex > 0 ? expenseItems[itemIndex - 1] : null)
    || null
}

export function resolveExpenseItemsForRiskCard(card, expenseItems = []) {
  const itemIds = [
    card?.itemId,
    card?.item_id,
    ...(Array.isArray(card?.itemIds) ? card.itemIds : []),
    ...(Array.isArray(card?.item_ids) ? card.item_ids : [])
  ].map(normalizeText).filter(Boolean)
  const invoiceId = normalizeText(card?.invoiceId || card?.invoice_id)
  const itemIndex = Number(card?.itemIndex || card?.item_index || 0)

  const matchedItems = []
  const appendItem = (item) => {
    if (!item || matchedItems.some((entry) => normalizeText(entry.id) === normalizeText(item.id))) {
      return
    }
    matchedItems.push(item)
  }

  if (itemIds.length) {
    expenseItems
      .filter((item) => itemIds.includes(normalizeText(item.id)))
      .forEach(appendItem)
  }
  if (matchedItems.length) {
    return matchedItems
  }

  appendItem(expenseItems.find((item) => invoiceId && normalizeText(item.invoiceId) === invoiceId))
  appendItem(itemIndex > 0 ? expenseItems[itemIndex - 1] : null)
  return matchedItems
}

export function isRiskCardMissingExpenseNote(card, expenseItems = []) {
  const items = resolveExpenseItemsForRiskCard(card, expenseItems)
  if (!items.length) {
    return true
  }
  return items.some((item) => !normalizeText(item.itemNote))
}

export function hasStandardAdjustmentForItem(item, standardAdjustmentMap = new Map()) {
  const itemId = normalizeText(item?.id)
  if (!itemId) {
    return false
  }
  return Boolean(item?.standardAdjustmentAccepted || standardAdjustmentMap.has(itemId))
}

function isRiskCardResolvedForSubmitter(card, expenseItems, standardAdjustmentMap) {
  if (normalizeText(card?.source) === STANDARD_ADJUSTMENT_RISK_SOURCE) {
    return true
  }
  return hasStandardAdjustmentForItem(
    resolveExpenseItemForRiskCard(card, expenseItems),
    standardAdjustmentMap
  )
}

export function filterSubmitterResolvedRiskCards({
  cards = [],
  businessStage = 'reimbursement',
  isCurrentApplicant = false,
  isPrivilegedRiskViewer = false,
  expenseItems = [],
  standardAdjustmentMap = new Map()
} = {}) {
  if (businessStage !== 'reimbursement' || !isCurrentApplicant || isPrivilegedRiskViewer) {
    return cards
  }
  return cards.filter((card) => !isRiskCardResolvedForSubmitter(card, expenseItems, standardAdjustmentMap))
}

function extractRiskCardMoneyValues(card) {
  const corpus = [
    card?.risk,
    card?.summary,
    card?.suggestion,
    card?.title,
    ...(Array.isArray(card?.ruleBasis) ? card.ruleBasis : [])
  ].map(normalizeText).filter(Boolean).join(' ')
  return [...corpus.matchAll(/(?:￥|¥)?\s*(\d+(?:,\d{3})*(?:\.\d+)?)\s*元/g)]
    .map((match) => Number(String(match[1] || '').replace(/,/g, '')))
    .filter((amount) => Number.isFinite(amount) && amount > 0)
}

function resolveParsedStandardAmount(card, item) {
  const originalAmount = normalizeAmount(item?.itemAmount)
  if (originalAmount <= 0) {
    return null
  }
  const candidates = extractRiskCardMoneyValues(card)
    .filter((amount) => amount > 0 && amount < originalAmount)
  return candidates.length ? Math.max(...candidates) : null
}

function extractRequestApplicationDays(request = {}) {
  return parseDayCount(
    request?.relatedApplication?.days
    || request?.application_days
    || request?.applicationDays
    || request?.days
  )
}

function extractRiskCardNightCount(card, request = {}) {
  const applicationDays = extractRequestApplicationDays(request)
  if (applicationDays) {
    return applicationDays
  }
  const corpus = [card?.risk, card?.summary, card?.suggestion, card?.title]
    .map(normalizeText)
    .join(' ')
  const match = corpus.match(/(\d+)\s*(?:晚|夜|间夜)/)
  return match ? Math.max(1, Number(match[1]) || 1) : 1
}

async function resolveTravelStandardAmount({ card, item, request, calculateTravelReimbursement }) {
  const itemType = normalizeText(item?.itemType)
  if (!['hotel_ticket', 'hotel'].includes(itemType)) {
    return null
  }
  const location = normalizeText(item?.itemLocation || request?.location || request?.sceneTarget)
  if (!location || typeof calculateTravelReimbursement !== 'function') {
    return null
  }
  const grade = normalizeText(request?.employeeGrade || request?.profileGrade)
  const days = extractRiskCardNightCount(card, request)
  try {
    const result = await calculateTravelReimbursement({ days, location, grade })
    const hotelAmount = Number(result?.hotel_amount ?? result?.hotelAmount)
    return Number.isFinite(hotelAmount) && hotelAmount > 0 ? hotelAmount : null
  } catch (error) {
    return null
  }
}

async function resolveRiskStandardReimbursableAmount({ card, item, request, calculateTravelReimbursement }) {
  const originalAmount = normalizeAmount(item?.itemAmount)
  if (originalAmount <= 0) {
    return 0
  }
  const parsedAmount = resolveParsedStandardAmount(card, item)
  if (parsedAmount !== null) {
    return Math.min(originalAmount, parsedAmount)
  }
  const travelStandardAmount = await resolveTravelStandardAmount({
    card,
    item,
    request,
    calculateTravelReimbursement
  })
  if (travelStandardAmount !== null) {
    return Math.min(originalAmount, travelStandardAmount)
  }
  return originalAmount
}

export async function buildStandardAdjustmentPayload({
  warnings = [],
  expenseItems = [],
  request = {},
  calculateTravelReimbursement
} = {}) {
  const risks = []
  for (const warning of warnings) {
    const item = resolveExpenseItemForRiskCard(warning, expenseItems)
    if (!item) {
      continue
    }
    const originalAmount = normalizeAmount(item.itemAmount)
    const reimbursableAmount = await resolveRiskStandardReimbursableAmount({
      card: warning,
      item,
      request,
      calculateTravelReimbursement
    })
    risks.push({
      risk_id: warning.id,
      item_id: item.id,
      title: warning.title,
      risk: warning.risk || warning.summary,
      application_days: extractRiskCardNightCount(warning, request),
      original_amount: originalAmount,
      reimbursable_amount: reimbursableAmount
    })
  }
  return { risks }
}
