import { ref } from 'vue'

import {
  APPLICATION_TRANSPORT_MODE_OPTIONS,
  applyApplicationPolicyEstimateError,
  applyApplicationPolicyEstimateResult,
  buildApplicationPreviewRows,
  buildApplicationPolicyEstimateRequest,
  buildLocalApplicationPreviewMessage,
  normalizeApplicationPreview,
  resolveApplicationDaysFromDateRange,
  refreshApplicationPreviewTransportEstimate
} from '../../utils/expenseApplicationPreview.js'
import { trackApplicationPreviewFieldEdit } from '../../utils/expenseApplicationDecisionFeedback.js'
import {
  buildWorkbenchDateLabel,
  canApplyWorkbenchDateSelection,
  getTodayDateValue
} from '../../utils/workbenchComposerDate.js'

const APPLICATION_ESTIMATE_INPUT_FIELD_KEYS = [
  'applicationType',
  'time',
  'location',
  'days',
  'transportMode',
  'grade'
]

const APPLICATION_ESTIMATE_RESULT_FIELD_KEYS = [
  'lodgingDailyCap',
  'subsidyDailyCap',
  'transportPolicy',
  'policyEstimate',
  'amount',
  'matchedCity',
  'ruleName',
  'ruleVersion',
  'hotelAmount',
  'allowanceAmount',
  'transportEstimatedAmount',
  'transportEstimateDate',
  'transportQueryLatencyMs',
  'transportEstimateSource',
  'transportEstimateConfidence',
  'policyTotalAmount'
]

function buildApplicationEstimateInputFingerprint(preview = {}) {
  const fields = preview?.fields || {}
  return JSON.stringify(
    APPLICATION_ESTIMATE_INPUT_FIELD_KEYS.map((fieldKey) => String(fields[fieldKey] || '').trim())
  )
}

function mergeApplicationEstimateResult(currentPreview = {}, refreshedPreview = {}) {
  const refreshedFields = refreshedPreview?.fields || {}
  const estimateFields = Object.fromEntries(
    APPLICATION_ESTIMATE_RESULT_FIELD_KEYS
      .filter((fieldKey) => Object.hasOwn(refreshedFields, fieldKey))
      .map((fieldKey) => [fieldKey, refreshedFields[fieldKey]])
  )
  return normalizeApplicationPreview({
    ...currentPreview,
    fields: {
      ...(currentPreview?.fields || {}),
      ...estimateFields
    },
    policyEstimate: refreshedPreview?.policyEstimate,
    policyEstimateStatus: refreshedPreview?.policyEstimateStatus
  })
}

function parseEditorDateMatches(value) {
  const text = String(value || '').trim()
  return [...text.matchAll(/20\d{2}[-/.]\d{1,2}[-/.]\d{1,2}/g)]
    .map((item) => normalizeEditorIsoDate(item[0]))
    .filter(Boolean)
}

function parseEditorDateValue(value) {
  const matches = parseEditorDateMatches(value)
  const startDate = matches[0] || getTodayDateValue()
  const endDate = matches[1] || startDate
  return {
    dateMode: matches.length > 1 && startDate !== endDate ? 'range' : 'single',
    singleDate: startDate,
    rangeStartDate: startDate,
    rangeEndDate: endDate
  }
}

function isApplicationPreviewDateField(fieldKey = '') {
  return ['time', 'time_return'].includes(String(fieldKey || '').trim())
}

function formatApplicationPreviewDateRange(startDate = '', endDate = '') {
  const start = String(startDate || '').trim()
  const end = String(endDate || '').trim()
  if (!start && !end) return ''
  if (!start) return end
  if (!end || end === start) return start
  return `${start} 至 ${end}`
}

function normalizeEditorIsoDate(value = '') {
  const match = String(value || '').trim().match(/^(20\d{2})[-/.](\d{1,2})[-/.](\d{1,2})$/)
  if (!match) return ''
  return buildEditorIsoDate(Number(match[1]), Number(match[2]), Number(match[3]))
}

function buildEditorIsoDate(year, month, day) {
  if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) return ''
  const date = new Date(Date.UTC(year, month - 1, day))
  if (
    Number.isNaN(date.getTime()) ||
    date.getUTCFullYear() !== year ||
    date.getUTCMonth() + 1 !== month ||
    date.getUTCDate() !== day
  ) {
    return ''
  }
  return [
    String(year).padStart(4, '0'),
    String(month).padStart(2, '0'),
    String(day).padStart(2, '0')
  ].join('-')
}

function parseEditorDateParts(value = '') {
  const normalized = normalizeEditorIsoDate(value)
  const match = normalized.match(/^(20\d{2})-(\d{2})-(\d{2})$/)
  if (!match) return null
  return {
    year: Number(match[1]),
    month: Number(match[2]),
    day: Number(match[3]),
    value: normalized
  }
}

function addDaysToEditorDate(value = '', offset = 0) {
  const parts = parseEditorDateParts(value)
  if (!parts) return ''
  const date = new Date(Date.UTC(parts.year, parts.month - 1, parts.day))
  date.setUTCDate(date.getUTCDate() + offset)
  return buildEditorIsoDate(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate())
}

function buildEditorDateFromMonthDay(referenceDate = '', month = 0, day = 0) {
  const reference = parseEditorDateParts(referenceDate) || parseEditorDateParts(getTodayDateValue())
  if (!reference) return ''
  const year = month < reference.month ? reference.year + 1 : reference.year
  return buildEditorIsoDate(year, month, day)
}

function buildEditorDateFromDay(referenceDate = '', day = 0) {
  const reference = parseEditorDateParts(referenceDate) || parseEditorDateParts(getTodayDateValue())
  if (!reference) return ''
  let year = reference.year
  let month = reference.month
  let candidate = buildEditorIsoDate(year, month, day)
  if (candidate && candidate < reference.value) {
    month += 1
    if (month > 12) {
      month = 1
      year += 1
    }
    candidate = buildEditorIsoDate(year, month, day)
  }
  return candidate
}

function normalizeEditorDateInput(value = '', referenceDate = '') {
  const text = String(value || '').trim()
  if (!text) return ''

  const fullDate = text.match(/20\d{2}[-/.]\d{1,2}[-/.]\d{1,2}/)
  if (fullDate) {
    return normalizeEditorIsoDate(fullDate[0])
  }

  const monthDay = text.match(/(\d{1,2})\s*月\s*(\d{1,2})\s*日?/) || text.match(/(\d{1,2})[/-](\d{1,2})/)
  if (monthDay) {
    return buildEditorDateFromMonthDay(referenceDate, Number(monthDay[1]), Number(monthDay[2]))
  }

  const dayOnly = text.match(/^(\d{1,2})\s*日?$/)
  if (dayOnly) {
    return buildEditorDateFromDay(referenceDate, Number(dayOnly[1]))
  }

  if (/大后天/.test(text)) return addDaysToEditorDate(referenceDate || getTodayDateValue(), 3)
  if (/后天/.test(text)) return addDaysToEditorDate(referenceDate || getTodayDateValue(), 2)
  if (/明天/.test(text)) return addDaysToEditorDate(referenceDate || getTodayDateValue(), 1)
  if (/今天/.test(text)) return normalizeEditorIsoDate(referenceDate || getTodayDateValue())
  return ''
}

function buildEmptyEditor() {
  return {
    messageId: '',
    fieldKey: '',
    draftValue: '',
    dateMode: 'single',
    singleDate: getTodayDateValue(),
    rangeStartDate: getTodayDateValue(),
    rangeEndDate: getTodayDateValue(),
    committing: false
  }
}

function resolveApplicationPreviewDateDraftValue(fieldKey = '', dateState = {}) {
  if (fieldKey === 'time_return') {
    return dateState.rangeEndDate || dateState.singleDate || getTodayDateValue()
  }
  return dateState.rangeStartDate || dateState.singleDate || getTodayDateValue()
}

function validateApplicationPreviewDateRange(value = '') {
  const dates = parseEditorDateMatches(value)
  if (dates.length < 2) {
    return {
      valid: true,
      message: ''
    }
  }
  const startDate = dates[0]
  const endDate = dates[dates.length - 1]
  if (startDate > endDate) {
    return {
      valid: false,
      message: '出发时间不能晚于返回时间，请重新选择。'
    }
  }
  return {
    valid: true,
    message: ''
  }
}

function shouldRefreshTransportEstimate(fieldKey) {
  return [
    'applicationType',
    'transportMode',
    'time',
    'time_return',
    'location',
    'days'
  ].includes(fieldKey)
}

function resolveEditorCurrentUser(currentUser) {
  if (currentUser && typeof currentUser === 'object' && 'value' in currentUser) {
    return currentUser.value || {}
  }
  return currentUser || {}
}

function buildEditedApplicationPreviewFields(fields = {}, editor = {}, nextValue = '') {
  const isDateField = isApplicationPreviewDateField(editor.fieldKey)
  const nextFields = {
    ...fields,
    [isDateField ? 'time' : editor.fieldKey]: nextValue
  }
  if (isDateField) {
    const resolvedDays = resolveApplicationDaysFromDateRange(nextValue)
    if (resolvedDays) {
      nextFields.days = resolvedDays
    }
  }
  return nextFields
}

function buildApplicationPreviewDateSelectionValue(editor = {}) {
  return buildWorkbenchDateLabel({
    mode: editor.dateMode,
    singleDate: editor.singleDate,
    rangeStartDate: editor.rangeStartDate,
    rangeEndDate: editor.rangeEndDate
  })
}

function buildApplicationPreviewTextDateValue(fields = {}, editor = {}) {
  const currentDateState = parseEditorDateValue(fields.time)
  const referenceDate = currentDateState.rangeStartDate || currentDateState.singleDate || getTodayDateValue()
  const typedDates = parseEditorDateMatches(editor.draftValue)
  if (typedDates.length >= 2) {
    return formatApplicationPreviewDateRange(typedDates[0], typedDates[typedDates.length - 1])
  }

  const typedDate = typedDates[0] || normalizeEditorDateInput(editor.draftValue, referenceDate) || String(editor.draftValue || '').trim()
  if (!typedDate) {
    return ''
  }

  if (editor.fieldKey === 'time_return') {
    return formatApplicationPreviewDateRange(currentDateState.rangeStartDate, typedDate)
  }

  const currentEndDate = currentDateState.rangeEndDate || typedDate
  return formatApplicationPreviewDateRange(typedDate, currentEndDate)
}

function buildApplicationPreviewEditorValue(fields = {}, editor = {}, options = {}) {
  if (!isApplicationPreviewDateField(editor.fieldKey)) {
    return String(editor.draftValue || '').trim()
  }
  return options.useDateSelection
    ? buildApplicationPreviewDateSelectionValue(editor)
    : buildApplicationPreviewTextDateValue(fields, editor)
}

function buildTransportEstimatePendingPreview(preview = {}) {
  const fields = preview?.fields || {}
  return normalizeApplicationPreview({
    ...preview,
    fields: {
      ...fields,
      transportPolicy: '正在预估交通费用...',
      policyEstimate: '正在同步费用测算...',
      transportEstimatedAmount: '查询中'
    }
  })
}

export function useApplicationPreviewEditor({
  persistSessionState,
  toast,
  calculateTravelReimbursement,
  currentUser
} = {}) {
  const applicationPreviewEditor = ref(buildEmptyEditor())
  const estimateRequestVersions = new WeakMap()

  async function refreshApplicationPreviewEstimate(preview = {}) {
    const user = resolveEditorCurrentUser(currentUser)
    const estimateRequest = buildApplicationPolicyEstimateRequest(preview, user)
    if (estimateRequest.canCalculate && typeof calculateTravelReimbursement === 'function') {
      try {
        const result = await calculateTravelReimbursement(estimateRequest.payload)
        return applyApplicationPolicyEstimateResult(preview, result, user)
      } catch (error) {
        console.warn('Application preview estimate refresh failed:', error)
        return applyApplicationPolicyEstimateError(preview, error, user)
      }
    }
    return refreshApplicationPreviewTransportEstimate(preview)
  }

  function resolveApplicationPreviewRows(message) {
    return buildApplicationPreviewRows(message?.applicationPreview || {})
  }

  function resolveApplicationPreviewEditorControl(fieldKey) {
    if (fieldKey === 'transportMode') return 'select'
    if (isApplicationPreviewDateField(fieldKey)) return 'date'
    return 'text'
  }

  function resolveApplicationPreviewEditorOptions(fieldKey) {
    return fieldKey === 'transportMode' ? APPLICATION_TRANSPORT_MODE_OPTIONS : []
  }

  function resolveApplicationPreviewEditorDateMin(message, fieldKey) {
    if (fieldKey !== 'time_return') return ''
    const dateState = parseEditorDateValue(message?.applicationPreview?.fields?.time)
    return dateState.rangeStartDate || dateState.singleDate || ''
  }

  function resolveApplicationPreviewEditorDateMax(message, fieldKey) {
    if (fieldKey !== 'time') return ''
    const dateState = parseEditorDateValue(message?.applicationPreview?.fields?.time)
    return dateState.dateMode === 'range' ? dateState.rangeEndDate : ''
  }

  function isApplicationPreviewEditing(message, fieldKey) {
    return (
      String(applicationPreviewEditor.value.messageId || '') === String(message?.id || '') &&
      applicationPreviewEditor.value.fieldKey === fieldKey
    )
  }

  function openApplicationPreviewEditor(message, fieldKey, value) {
    if (!message?.applicationPreview || !fieldKey) return
    const targetRow = buildApplicationPreviewRows(message.applicationPreview)
      .find((row) => row.key === fieldKey)
    if (targetRow && targetRow.editable === false) return
    const normalizedValue = String(value || '').trim() === '待补充' ? '' : String(value || '')
    const fields = message.applicationPreview.fields || {}
    const dateState = isApplicationPreviewDateField(fieldKey)
      ? parseEditorDateValue(fields.time || normalizedValue)
      : {}
    const draftValue = isApplicationPreviewDateField(fieldKey)
      ? resolveApplicationPreviewDateDraftValue(fieldKey, dateState)
      : fieldKey === 'transportMode' && !APPLICATION_TRANSPORT_MODE_OPTIONS.includes(normalizedValue)
        ? ''
        : normalizedValue
    applicationPreviewEditor.value = {
      messageId: String(message.id || ''),
      fieldKey,
      draftValue,
      committing: false,
      ...dateState
    }
  }

  function cancelApplicationPreviewEditor() {
    applicationPreviewEditor.value = buildEmptyEditor()
  }

  function isApplicationPreviewDateEditorOpen(message) {
    return (
      isApplicationPreviewEditing(message, 'time') ||
      isApplicationPreviewEditing(message, 'time_return')
    )
  }

  function setApplicationPreviewDateMode(mode) {
    applicationPreviewEditor.value.dateMode = mode === 'range' ? 'range' : 'single'
  }

  function canApplyApplicationPreviewDateSelection() {
    const editor = applicationPreviewEditor.value
    return canApplyWorkbenchDateSelection({
      mode: editor.dateMode,
      singleDate: editor.singleDate,
      rangeStartDate: editor.rangeStartDate,
      rangeEndDate: editor.rangeEndDate
    })
  }

  async function commitApplicationPreviewEditor(message, options = {}) {
    const editor = applicationPreviewEditor.value
    if (editor.committing) {
      return false
    }
    if (!message?.applicationPreview || String(editor.messageId || '') !== String(message.id || '') || !editor.fieldKey) {
      cancelApplicationPreviewEditor()
      return false
    }
    applicationPreviewEditor.value = {
      ...editor,
      committing: true
    }

    const nextValue = buildApplicationPreviewEditorValue(
      message.applicationPreview.fields || {},
      editor,
      options
    )
    if (isApplicationPreviewDateField(editor.fieldKey) && !nextValue) {
      toast?.('请先选择有效日期。')
      applicationPreviewEditor.value = {
        ...applicationPreviewEditor.value,
        committing: false
      }
      return false
    }
    const dateValidation = isApplicationPreviewDateField(editor.fieldKey)
      ? validateApplicationPreviewDateRange(nextValue)
      : { valid: true, message: '' }
    if (!dateValidation.valid) {
      toast?.(dateValidation.message || '请确认返回时间不早于出发时间。')
      applicationPreviewEditor.value = {
        ...applicationPreviewEditor.value,
        committing: false
      }
      return false
    }
    const isDateField = isApplicationPreviewDateField(editor.fieldKey)
    const nextPreview = trackApplicationPreviewFieldEdit(
      message.applicationPreview,
      normalizeApplicationPreview({
        ...message.applicationPreview,
        fields: buildEditedApplicationPreviewFields(
          message.applicationPreview.fields || {},
          editor,
          nextValue
        )
      }),
      isDateField ? [editor.fieldKey, 'days'] : editor.fieldKey
    )
    const needRefreshEstimate = shouldRefreshTransportEstimate(editor.fieldKey)
    message.applicationPreview = needRefreshEstimate
      ? buildTransportEstimatePendingPreview(nextPreview)
      : nextPreview
    message.text = buildLocalApplicationPreviewMessage(message.applicationPreview)
    cancelApplicationPreviewEditor()
    persistSessionState?.()
    if (needRefreshEstimate) {
      const requestVersion = (estimateRequestVersions.get(message) || 0) + 1
      estimateRequestVersions.set(message, requestVersion)
      const estimateInputFingerprint = buildApplicationEstimateInputFingerprint(nextPreview)
      const refreshedPreview = await refreshApplicationPreviewEstimate(nextPreview)
      const isLatestRequest = estimateRequestVersions.get(message) === requestVersion
      const currentFingerprint = buildApplicationEstimateInputFingerprint(
        message.applicationPreview
      )
      if (!isLatestRequest || currentFingerprint !== estimateInputFingerprint) {
        return true
      }
      const mergedPreview = mergeApplicationEstimateResult(
        message.applicationPreview,
        refreshedPreview
      )
      message.applicationPreview = mergedPreview
      message.text = buildLocalApplicationPreviewMessage(mergedPreview)
      persistSessionState?.()
      toast?.('已更新出行方式和费用测算。')
      return true
    }
    toast?.('已更新核对表内容。')
    return true
  }

  async function commitApplicationPreviewDateEditor(message) {
    if (!canApplyApplicationPreviewDateSelection()) {
      toast?.('请确认结束日期不早于开始日期。')
      return false
    }
    return commitApplicationPreviewEditor(message, { useDateSelection: true })
  }

  function handleApplicationPreviewEditorKeydown(event, message) {
    if (event.key === 'Enter') {
      event.preventDefault()
      commitApplicationPreviewEditor(message)
      return
    }
    if (event.key === 'Escape') {
      event.preventDefault()
      cancelApplicationPreviewEditor()
    }
  }

  return {
    applicationPreviewEditor,
    resolveApplicationPreviewRows,
    resolveApplicationPreviewEditorControl,
    resolveApplicationPreviewEditorOptions,
    resolveApplicationPreviewEditorDateMin,
    resolveApplicationPreviewEditorDateMax,
    refreshApplicationPreviewEstimate,
    isApplicationPreviewEditing,
    isApplicationPreviewDateEditorOpen,
    openApplicationPreviewEditor,
    commitApplicationPreviewEditor,
    commitApplicationPreviewDateEditor,
    cancelApplicationPreviewEditor,
    setApplicationPreviewDateMode,
    canApplyApplicationPreviewDateSelection,
    handleApplicationPreviewEditorKeydown
  }
}
