import { readError } from '@/shared/utils/error'
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'

import { fetchFileBlobByURL, uploadFile } from '@/shared/api/files'
import {
  emptyListingPublishAgreements,
  emptyListingPublishOptions,
  emptyListingSalePriceConfig,
  fetchListingPublishAgreements,
  fetchListingPublishOptions,
  fetchListingSalePriceConfig,
  type ChargeMode,
  type ListingPublishAgreements,
  type ListingPublishOptions,
  type PublishSalePriceConfig,
  type ScreenshotKey,
} from '@/features/listings/api/listingOptions'
import {
  createListing,
  fetchSellerListing,
  updateListing,
  type Listing,
} from '@/features/listings/api/listings'
import { usePricingCalculator } from '@/shared/composables/usePricingCalculator'
import {
  buildPublishDraft,
  clearRecord,
  defaultPublishForm,
  readPublishDraft,
  removePublishDraft,
  writePublishDraft,
} from '@/features/seller/composables/usePublishDraft'
import type { PublishForm } from '@/shared/types/publish'
import { yuanToCent } from '@/shared/utils/money'
import {
  commonOnlineTimes,
  dailyLossOptions,
  formatNumber,
  roundRatio,
} from '@/shared/utils/pricing'

const draftSaveDelay = 400
const multiScreenshotLimit = 3
const multiScreenshotKeys = new Set(['tencentSecurity', 'skin'])

interface UsePublishFormOptions {
  draftKey: string
  submitSuccessPath: string
  persistBeforeUnload?: boolean
  confirmReset?: () => Promise<void>
  notifySuccess: (message: string) => void
  notifyWarning: (message: string) => void
  notifyError: (message: string) => void
}

export function usePublishForm(options: UsePublishFormOptions) {
  const router = useRouter()
  const route = useRoute()
  const loading = ref(false)
  const uploading = ref(false)
  const suppressDraftSave = ref(false)
  const draftReady = ref(false)
  const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
  const salePriceConfig = ref<PublishSalePriceConfig>(emptyListingSalePriceConfig)
  const publishAgreements = ref<ListingPublishAgreements>(emptyListingPublishAgreements)
  const virtualAssetSaleAgreementChecked = ref(false)
  const sellerAgreementChecked = ref(false)
  const fileInput = ref<HTMLInputElement | null>(null)
  const activeUploadKey = ref<ScreenshotKey>('coin')
  const form = reactive<PublishForm>(defaultPublishForm())
  const quantityValues = reactive<Record<string, number>>({})
  const quantityModes = reactive<Record<string, ChargeMode>>({})
  const screenshotFiles = reactive<Record<string, string[]>>({})
  const screenshotPreviews = reactive<Record<string, string[]>>({})
  const selectedSkins = ref<string[]>([])
  let draftSaveTimer: number | undefined
  const editListingID = computed(() => Number(route.params.id || 0))
  const isEditMode = computed(() => Number.isFinite(editListingID.value) && editListingID.value > 0)
  const pageTitle = computed(() => (isEditMode.value ? '编辑账号' : '发布账号'))
  const submitButtonText = computed(() => (isEditMode.value ? '提交修改' : '立即发布'))
  const submitLoadingText = computed(() => (isEditMode.value ? '提交中...' : '发布中...'))

  const pricing = usePricingCalculator({
    form,
    publishOptions,
    salePriceConfig,
    quantityValues,
    quantityModes,
    screenshotFiles,
    selectedSkins,
  })
  const uploadedScreenshotCount = computed(
    () => pricing.screenshotSlots.value.filter(item => getScreenshotCount(item.key) > 0).length
  )
  const requiredScreenshotCount = ref(0)
  const canPublishAfterAgreements = computed(
    () => virtualAssetSaleAgreementChecked.value && sellerAgreementChecked.value
  )
  const disabledOnlineStartOptions = computed(() =>
    commonOnlineTimes.filter(time => !canUseOnlineStart(time))
  )
  const disabledOnlineEndOptions = computed(() =>
    commonOnlineTimes.filter(time => !canUseOnlineEnd(time))
  )

  onMounted(async () => {
    if (!isEditMode.value) restoreDraft()
    if (options.persistBeforeUnload) window.addEventListener('beforeunload', handleBeforeUnload)
    await loadPublishOptions()
    if (isEditMode.value) {
      await loadEditingListing()
    }
    draftReady.value = true
  })

  onBeforeUnmount(() => {
    saveDraft({ force: true })
    if (options.persistBeforeUnload) window.removeEventListener('beforeunload', handleBeforeUnload)
    revokeAllScreenshotPreviews()
  })

  watch(
    [form, quantityValues, quantityModes, screenshotFiles, selectedSkins],
    () => {
      saveDraft()
    },
    { deep: true }
  )

  watch(
    pricing.recommendedDepositAmount,
    () => {
      syncRecommendedDeposit()
    },
    { immediate: true }
  )

  watch(
    [() => form.season_insurance, pricing.quantityItems],
    () => {
      clearForbiddenQuantityItems()
    },
    { deep: true }
  )

  watch(
    [pricing.screenshotSlots, () => form.ban_record],
    () => {
      requiredScreenshotCount.value = pricing.screenshotSlots.value.filter(item =>
        isScreenshotRequired(item)
      ).length
    },
    { immediate: true, deep: true }
  )

  async function loadPublishOptions() {
    try {
      const [nextPublishOptions, nextSalePriceConfig, nextPublishAgreements] = await Promise.all([
        fetchListingPublishOptions(),
        fetchListingSalePriceConfig(),
        fetchListingPublishAgreements(),
      ])
      publishOptions.value = nextPublishOptions
      salePriceConfig.value = nextSalePriceConfig
      publishAgreements.value = nextPublishAgreements

      // 默认数字都是0
      for (const item of nextPublishOptions.quantity_items) {
        if (quantityValues[item.key] === undefined) {
          quantityValues[item.key] = 0
        }
      }
    } catch {
      publishOptions.value = emptyListingPublishOptions
      salePriceConfig.value = emptyListingSalePriceConfig
      publishAgreements.value = emptyListingPublishAgreements
    }
  }

  async function loadEditingListing() {
    if (!isEditMode.value) return
    loading.value = true
    try {
      const listing = await fetchSellerListing(editListingID.value)
      applyListingToForm(listing)
    } catch (error) {
      options.notifyError(readError(error, '商品信息加载失败'))
      await router.push(options.submitSuccessPath)
    } finally {
      loading.value = false
    }
  }

  function saveDraft(saveOptions: { force?: boolean } = {}) {
    if (isEditMode.value) return
    if (suppressDraftSave.value) {
      clearDraftSaveTimer()
      return
    }
    if (!saveOptions.force && !draftReady.value) return
    if (!saveOptions.force) {
      scheduleDraftSave()
      return
    }
    clearDraftSaveTimer()
    writeDraft()
  }

  function scheduleDraftSave() {
    clearDraftSaveTimer()
    draftSaveTimer = window.setTimeout(() => {
      draftSaveTimer = undefined
      writeDraft()
    }, draftSaveDelay)
  }

  function clearDraftSaveTimer() {
    if (draftSaveTimer === undefined) return
    window.clearTimeout(draftSaveTimer)
    draftSaveTimer = undefined
  }

  function writeDraft() {
    writePublishDraft(
      options.draftKey,
      buildPublishDraft({
        form,
        quantityValues,
        quantityModes,
        screenshotFiles,
        selectedSkins: selectedSkins.value,
      })
    )
  }

  function handleSaveDraft() {
    saveDraft({ force: true })
    options.notifySuccess('草稿已保存')
  }

  function handleBeforeUnload() {
    saveDraft({ force: true })
  }

  function restoreDraft() {
    const draft = readPublishDraft(options.draftKey)
    if (!draft) return
    Object.assign(form, draft.form)
    normalizeOnlineTime()
    clearRecord(quantityValues)
    clearRecord(quantityModes)
    clearRecord(screenshotFiles)
    Object.assign(quantityValues, draft.quantityValues)
    Object.assign(quantityModes, draft.quantityModes)
    Object.assign(screenshotFiles, draft.screenshotFiles)
    selectedSkins.value = draft.selectedSkins
    hydrateScreenshotPreviews()
  }

  function applyListingToForm(listing: Listing) {
    const summary = recordValue(listing.asset_summary)
    const onlineTime = recordValue(summary.online_time)
    const screenshotGroups = recordValue(summary.screenshot_groups)
    const skinGroups = recordValue(summary.skin_groups)
    const breakdown = recordValue(summary.price_breakdown)

    clearRecord(quantityValues)
    clearRecord(quantityModes)
    clearRecord(screenshotFiles)
    revokeAllScreenshotPreviews()

    Object.assign(form, {
      server_region: listing.server_region || '',
      face_owner: stringValue(summary.face_owner),
      haf_coin_amount: listing.haf_coin_amount > 0 ? listing.haf_coin_amount / 1000000 : '',
      rank_level: listing.rank_level || '',
      secret_kd: stringValue(summary.secret_kd),
      fire_level: numberOrEmpty(summary.fire_level),
      daily_loss_m: numberOrDefault(summary.daily_loss_m, 10),
      accelerated_sale_ratio: numberOrEmpty(breakdown.accelerated_sale_ratio),
      season_insurance: stringValue(summary.season_insurance),
      stamina_level: stringValue(summary.stamina_level),
      load_level: stringValue(summary.load_level),
      login_method: listing.login_platform || '',
      online_start: stringValue(onlineTime.start),
      online_end: stringValue(onlineTime.end),
      unlock_said: stringValue(summary.unlock_said),
      ban_record: stringValue(summary.ban_record),
      common_regions: stringArray(summary.common_regions),
      depositAmountYuan: listing.deposit_amount_cent > 0 ? listing.deposit_amount_cent / 100 : '',
      remark: listing.description || stringValue(summary.remark),
    })

    for (const item of publishOptions.value.quantity_items) {
      quantityValues[item.key] = 0
    }
    for (const item of arrayValue(summary.resources)) {
      const resource = recordValue(item)
      const key = stringValue(resource.key)
      if (!key) continue
      quantityValues[key] = numberOrDefault(resource.quantity, 0)
      const mode = stringValue(resource.mode)
      quantityModes[key] = mode === '赠送' ? '赠送' : '收费'
    }

    for (const [key, urls] of Object.entries(screenshotGroups)) {
      const cleaned = stringArray(urls)
      if (cleaned.length) screenshotFiles[key] = cleaned
    }
    if (!Object.keys(screenshotFiles).length && listing.screenshot_urls.length) {
      screenshotFiles.coin = [...listing.screenshot_urls]
    }

    selectedSkins.value = Object.values(skinGroups).flatMap(value => stringArray(value))
    virtualAssetSaleAgreementChecked.value = true
    sellerAgreementChecked.value = true
    hydrateScreenshotPreviews()
  }

  function resetDraftState() {
    Object.assign(form, defaultPublishForm())
    clearRecord(quantityValues)
    clearRecord(quantityModes)
    clearRecord(screenshotFiles)
    revokeAllScreenshotPreviews()
    selectedSkins.value = []
    virtualAssetSaleAgreementChecked.value = false
    sellerAgreementChecked.value = false
    activeUploadKey.value = 'coin'

    // Also re-initialize quantityValues to 0
    for (const item of publishOptions.value.quantity_items) {
      quantityValues[item.key] = 0
    }
  }

  function normalizeOnlineTime() {
    if (form.online_start === '全天' || form.online_end === '全天') {
      form.online_start = '00:00'
      form.online_end = '23:59'
    }
  }

  function isAllDayOnline() {
    return form.online_start === '00:00' && form.online_end === '23:59'
  }

  function isOnlineStartPresetActive(value: string) {
    return !isAllDayOnline() && form.online_start === value
  }

  function isOnlineEndPresetActive(value: string) {
    return !isAllDayOnline() && form.online_end === value
  }

  function selectAllDayOnline() {
    form.online_start = '00:00'
    form.online_end = '23:59'
  }

  function selectOnlineStart(value: string | number) {
    const time = String(value)
    if (!canUseOnlineStart(time)) {
      options.notifyWarning('开始时间必须早于结束时间')
      return
    }
    form.online_start = time
  }

  function selectOnlineEnd(value: string | number) {
    const time = String(value)
    if (!canUseOnlineEnd(time)) {
      options.notifyWarning('结束时间必须晚于开始时间')
      return
    }
    form.online_end = time
  }

  function handleOnlineStartChange(value: string | number | null | undefined) {
    const time = normalizeOnlineTimeValue(value)
    form.online_start = time
    const start = parseOnlineTime(time)
    if (start === null) return
    const lastTime = parseOnlineTime('23:59')
    if (lastTime !== null && start >= lastTime) {
      form.online_start = ''
      options.notifyWarning('开始时间必须早于结束时间')
      return
    }
    const end = parseOnlineTime(form.online_end)
    if (end !== null && start >= end) {
      form.online_end = ''
      options.notifyWarning('结束时间已清空，请选择晚于开始时间的结束时间')
    }
  }

  function handleOnlineEndChange(value: string | number | null | undefined) {
    const time = normalizeOnlineTimeValue(value)
    form.online_end = time
    const end = parseOnlineTime(time)
    if (end === null) return
    const firstTime = parseOnlineTime('00:00')
    if (firstTime !== null && end <= firstTime) {
      form.online_end = ''
      options.notifyWarning('结束时间必须晚于开始时间')
      return
    }
    const start = parseOnlineTime(form.online_start)
    if (start !== null && end <= start) {
      form.online_end = ''
      options.notifyWarning('结束时间必须晚于开始时间')
    }
  }

  function canUseOnlineStart(value: string) {
    const start = parseOnlineTime(value)
    const lastTime = parseOnlineTime('23:59')
    if (start === null || lastTime === null || start >= lastTime) return false
    const end = parseOnlineTime(form.online_end)
    return end === null || start < end
  }

  function canUseOnlineEnd(value: string) {
    const end = parseOnlineTime(value)
    const firstTime = parseOnlineTime('00:00')
    if (end === null || firstTime === null || end <= firstTime) return false
    const start = parseOnlineTime(form.online_start)
    return start === null || end > start
  }

  function validateOnlineTime() {
    const start = parseOnlineTime(form.online_start)
    const end = parseOnlineTime(form.online_end)
    if (start === null) return '请选择在线开始时间'
    if (end === null) return '请选择在线结束时间'
    if (start === end) return '在线开始和结束时间不能相同'
    if (end < start) return '在线结束时间必须晚于开始时间'
    return ''
  }

  async function handleResetDraft() {
    if (isEditMode.value) {
      await loadEditingListing()
      options.notifySuccess('已恢复原商品信息')
      return
    }
    try {
      await options.confirmReset?.()
    } catch {
      return
    }
    suppressDraftSave.value = true
    clearDraftSaveTimer()
    resetDraftState()
    removePublishDraft(options.draftKey)
    options.notifySuccess('已重置')
    window.setTimeout(() => {
      suppressDraftSave.value = false
    })
  }

  function toggleSkin(skin: string) {
    selectedSkins.value = selectedSkins.value.includes(skin)
      ? selectedSkins.value.filter(item => item !== skin)
      : [...selectedSkins.value, skin]
  }

  function toggleRegion(region: string) {
    form.common_regions = form.common_regions.includes(region)
      ? form.common_regions.filter(item => item !== region)
      : [...form.common_regions, region]
  }

  function triggerUpload(key: ScreenshotKey) {
    if (!canAddScreenshot(key)) {
      options.notifyWarning(`${getScreenshotLabel(key)}最多上传${getScreenshotLimit(key)}张`)
      return
    }
    activeUploadKey.value = key
    fileInput.value?.click()
  }

  async function handleScreenshotUpload(event: Event) {
    const input = event.target as HTMLInputElement
    const files = Array.from(input.files || [])
    if (!files.length) return
    const key = activeUploadKey.value
    const availableCount = getScreenshotLimit(key) - getScreenshotCount(key)
    if (availableCount <= 0) {
      options.notifyWarning(`${getScreenshotLabel(key)}最多上传${getScreenshotLimit(key)}张`)
      input.value = ''
      return
    }
    const uploadFiles = files.slice(0, availableCount)
    if (files.length > availableCount) {
      options.notifyWarning(
        `${getScreenshotLabel(key)}最多上传${getScreenshotLimit(key)}张，本次仅上传${availableCount}张`
      )
    }
    uploading.value = true
    try {
      for (const file of uploadFiles) {
        const previewURL = URL.createObjectURL(file)
        try {
          const uploaded = await uploadFile(file, 'listing')
          appendScreenshot(key, uploaded.url, previewURL)
        } catch (error) {
          URL.revokeObjectURL(previewURL)
          throw error
        }
      }
      options.notifySuccess(
        uploadFiles.length > 1 ? `已上传${uploadFiles.length}张截图` : '截图已上传'
      )
    } catch (error) {
      options.notifyError(readError(error, '截图上传失败'))
    } finally {
      uploading.value = false
      input.value = ''
    }
  }

  function removeScreenshot(key: ScreenshotKey, index = 0) {
    revokeScreenshotPreview(key, index)
    screenshotFiles[key]?.splice(index, 1)
    if (!screenshotFiles[key]?.length) delete screenshotFiles[key]
  }

  function getScreenshotPreviewURL(key: ScreenshotKey, index = 0) {
    return screenshotPreviews[key]?.[index] || screenshotFiles[key]?.[index] || ''
  }

  function appendScreenshot(key: ScreenshotKey, fileURL: string, previewURL: string) {
    if (!screenshotFiles[key]) screenshotFiles[key] = []
    if (!screenshotPreviews[key]) screenshotPreviews[key] = []
    screenshotFiles[key].push(fileURL)
    screenshotPreviews[key].push(previewURL)
  }

  function setScreenshotPreview(key: ScreenshotKey, index: number, previewURL: string) {
    revokeScreenshotPreview(key, index)
    if (!screenshotPreviews[key]) screenshotPreviews[key] = []
    screenshotPreviews[key][index] = previewURL
  }

  function revokeScreenshotPreview(key: ScreenshotKey, index: number) {
    const previewURL = screenshotPreviews[key]?.[index]
    if (previewURL?.startsWith('blob:')) URL.revokeObjectURL(previewURL)
    screenshotPreviews[key]?.splice(index, 1)
    if (!screenshotPreviews[key]?.length) delete screenshotPreviews[key]
  }

  function revokeAllScreenshotPreviews() {
    for (const [key, previews] of Object.entries(screenshotPreviews)) {
      for (const previewURL of previews) {
        if (previewURL?.startsWith('blob:')) URL.revokeObjectURL(previewURL)
      }
      delete screenshotPreviews[key]
    }
  }

  async function hydrateScreenshotPreviews() {
    for (const [key, fileURLs] of Object.entries(screenshotFiles)) {
      for (const [index, fileURL] of fileURLs.entries()) {
        if (
          !fileURL ||
          screenshotPreviews[key]?.[index] ||
          !fileURL.startsWith('/api/files/object')
        )
          continue
        try {
          const blob = await fetchFileBlobByURL(fileURL)
          setScreenshotPreview(key, index, URL.createObjectURL(blob))
        } catch {
          // 草稿预览失败不影响已上传文件地址，提交时仍会带上原 URL。
        }
      }
    }
  }

  function handleFireLevelInput(value: string | number | undefined) {
    if (value === '' || value === undefined) {
      form.fire_level = ''
      return
    }
    const level = Number(value)
    form.fire_level = Number.isFinite(level) ? Math.trunc(level) : ''
  }

  function handleAcceleratedSaleRatioInput(value: string | number | undefined) {
    if (value === '' || value === undefined) {
      form.accelerated_sale_ratio = ''
      return
    }
    const ratio = Number(value)
    form.accelerated_sale_ratio = Number.isFinite(ratio) ? ratio : ''
  }

  function syncRecommendedDeposit() {
    const recommended = pricing.recommendedDepositAmount.value
    if (recommended <= 0) return
    const current = Number(form.depositAmountYuan)
    if (form.depositAmountYuan === '' || !Number.isFinite(current) || current < recommended) {
      form.depositAmountYuan = recommended
    }
  }

  function useRecommendedDeposit() {
    if (pricing.recommendedDepositAmount.value > 0) {
      form.depositAmountYuan = pricing.recommendedDepositAmount.value
    }
  }

  // 失焦时实时校正：押金低于（或低于等于无效/为空）智能推荐时自动补到推荐值并提示。
  function handleDepositBlur() {
    const recommended = pricing.recommendedDepositAmount.value
    if (recommended <= 0) return
    const current = Number(form.depositAmountYuan)
    if (form.depositAmountYuan === '' || !Number.isFinite(current) || current < recommended) {
      form.depositAmountYuan = recommended
      options.notifyWarning(`押金不能低于智能推荐 ¥${recommended}，已自动调整`)
    }
  }

  function clearForbiddenQuantityItems() {
    for (const item of pricing.quantityItems.value) {
      if (!pricing.isQuantityItemDisabled(item)) continue
      quantityValues[item.key] = 0
      quantityModes[item.key] = '赠送'
    }
  }

  function setQuantityMode(item: { key: string; label: string }, mode: ChargeMode) {
    if (pricing.isQuantityItemDisabled(item)) return
    quantityModes[item.key] = mode
  }

  function clampAcceleratedSaleRatioInput() {
    if (!pricing.hasAcceleratedSaleRatioInput() || pricing.calculatedDefaultSaleRatio.value <= 0)
      return
    const ratio = Number(form.accelerated_sale_ratio)
    if (!Number.isFinite(ratio)) {
      form.accelerated_sale_ratio = ''
      return
    }
    form.accelerated_sale_ratio = roundRatio(
      Math.min(
        Math.max(ratio, pricing.calculatedDefaultSaleRatio.value),
        pricing.maxAcceleratedSaleRatio.value
      )
    )
  }

  function useReferenceSaleRatio() {
    form.accelerated_sale_ratio = ''
  }

  function useMaxAcceleratedSaleRatio() {
    if (pricing.maxAcceleratedSaleRatio.value <= 0) return
    form.accelerated_sale_ratio = pricing.maxAcceleratedSaleRatio.value
  }

  async function handleSubmit() {
    normalizeOnlineTime()
    const error = validateForm()
    if (error) {
      options.notifyWarning(error)
      return
    }
    loading.value = true
    try {
      const payload = {
        title: pricing.publishTitle.value,
        description: form.remark,
        server_region: form.server_region,
        login_platform: form.login_method,
        rank_level: form.rank_level,
        haf_coin_amount: pricing.coinMAmount.value * 1000000,
        asset_summary: buildAssetSummary(),
        screenshot_urls: pricing.screenshotUrls.value,
        price_cent: yuanToCent(pricing.calculatedFinalPrice.value),
        deposit_amount_cent: yuanToCent(Number(form.depositAmountYuan)),
        agreed_virtual_asset_sale: virtualAssetSaleAgreementChecked.value,
        agreed_seller_agreement: sellerAgreementChecked.value,
      }
      const listing = isEditMode.value
        ? await updateListing(editListingID.value, payload)
        : await createListing(payload)
      if (!isEditMode.value) removePublishDraft(options.draftKey)
      suppressDraftSave.value = true
      clearDraftSaveTimer()
      options.notifySuccess(
        listing.status === 'published' && listing.review_status === 'approved'
          ? isEditMode.value
            ? '修改成功，已上架'
            : '发布成功，已上架'
          : isEditMode.value
            ? '修改成功，等待后台审核'
            : '发布成功，等待后台审核'
      )
      await router.push(options.submitSuccessPath)
    } catch (error) {
      options.notifyError(
        readError(
          error,
          isEditMode.value ? '修改失败，请稍后重试' : '发布失败，请确认已登录并完成实名认证'
        )
      )
    } finally {
      loading.value = false
    }
  }

  function validateForm() {
    if (!form.server_region) return '请选择区服'
    if (pricing.coinMAmount.value <= 0) return '请填写哈夫币/M'
    if (!form.rank_level) return '请选择段位'
    if (!form.fire_level) return '请填写烽火等级'
    if (Number(form.fire_level) < pricing.fireLevelMin.value)
      return `烽火等级低于${pricing.fireLevelMin.value}级的号无法发布`
    if (!form.season_insurance) return '请选择赛季保险'
    if (!form.stamina_level) return '请选择体力等级'
    if (!form.load_level) return '请选择负重等级'
    if (!dailyLossOptions.includes(pricing.dailyLossMAmount.value)) return '请选择每日损耗'
    const onlineTimeError = validateOnlineTime()
    if (onlineTimeError) return onlineTimeError
    if (pricing.hasAcceleratedSaleRatioInput()) {
      const ratio = Number(form.accelerated_sale_ratio)
      if (!Number.isFinite(ratio) || ratio <= 0) return '加速出售比例格式不正确'
      if (
        pricing.calculatedDefaultSaleRatio.value > 0 &&
        ratio < pricing.calculatedDefaultSaleRatio.value
      ) {
        return `加速出售比例不能低于默认比例 1:${formatNumber(pricing.calculatedDefaultSaleRatio.value)}`
      }
      if (
        pricing.maxAcceleratedSaleRatio.value > 0 &&
        ratio > pricing.maxAcceleratedSaleRatio.value
      ) {
        return `加速出售比例不能超过 1:${formatNumber(pricing.maxAcceleratedSaleRatio.value)}`
      }
    }
    if (pricing.banRecordOptions.value.length && !form.ban_record) return '请选择封禁记录'
    if (!form.unlock_said) return '请选择是否解锁赛伊德'
    if (form.depositAmountYuan === '' || Number(form.depositAmountYuan) < 0) return '请填写押金'
    if (!Number.isFinite(Number(form.depositAmountYuan))) return '押金格式不正确'
    if (
      pricing.recommendedDepositAmount.value > 0 &&
      Number(form.depositAmountYuan) < pricing.recommendedDepositAmount.value
    ) {
      return `押金不能低于智能推荐 ¥${pricing.recommendedDepositAmount.value}`
    }
    if (
      pricing.calculatedConsumablePrice.value > 0 &&
      Number(form.depositAmountYuan) <= pricing.calculatedConsumablePrice.value
    ) {
      return `押金必须大于额外消耗品总价值 ¥${pricing.calculatedConsumablePrice.value}`
    }
    if (!pricing.calculatedFinalPrice.value) return '请完善币数、保险、体力和负重后再发布'
    if (!Number.isFinite(pricing.calculatedFinalPrice.value))
      return '发布价格计算异常，请检查填写内容'
    if (!canPublishAfterAgreements.value) return '请先阅读并勾选两份发布协议'
    for (const item of pricing.screenshotSlots.value) {
      if (isScreenshotRequired(item) && getScreenshotCount(item.key) === 0)
        return `请上传${item.label}`
    }
    for (const item of pricing.quantityItems.value) {
      if (!pricing.isQuantityItemDisabled(item)) {
        const val = quantityValues[item.key]
        if (val === undefined || val === null || (val as unknown) === '') {
          return `请填写${item.label}的数量`
        }
        if (Number(val) < 0) {
          return `${item.label}的数量不能为负数`
        }
      }
    }
    for (const item of pricing.quantityItems.value) {
      if (pricing.isQuantityItemDisabled(item) && Number(quantityValues[item.key] || 0) > 0) {
        return '赛季保险选择 3*3 时不能填写 9格体验卡'
      }
    }
    return ''
  }

  function isScreenshotRequired(item: { key: string; required: boolean }) {
    return item.required || (item.key === 'tencentSecurity' && shouldRequireBanEvidence())
  }

  function shouldRequireBanEvidence() {
    return pricing.banEvidenceOptions.value.includes(form.ban_record)
  }

  function buildAssetSummary() {
    return {
      face_owner: form.face_owner,
      secret_kd: form.secret_kd,
      fire_level: Number(form.fire_level),
      daily_loss_m: pricing.dailyLossMAmount.value,
      publish_ratio: pricing.calculatedPlatformPricing.value.buyerRatio,
      price_breakdown: {
        seller_reference_ratio: pricing.calculatedSellerReferenceRatio.value,
        seller_ratio: pricing.calculatedRatio.value,
        seller_coin_base_price: pricing.calculatedCoinBasePrice.value,
        seller_total_price: pricing.calculatedSellerPrice.value,
        consumable_price: pricing.calculatedConsumablePrice.value,
        daily_loss_ratio_adjustment: pricing.dailyLossRatioAdjustment.value,
        accelerated_sale_ratio: pricing.hasAcceleratedSaleRatioInput()
          ? Number(form.accelerated_sale_ratio)
          : pricing.calculatedDefaultSaleRatio.value,
        buyer_coin_base_price: pricing.calculatedPlatformPricing.value.buyerCoinBasePrice,
        buyer_total_price: pricing.calculatedFinalPrice.value,
        buyer_ratio: pricing.calculatedPlatformPricing.value.buyerRatio,
        platform_markup_amount: pricing.calculatedPlatformPricing.value.platformMarkupAmount,
        platform_rule_type: pricing.calculatedPlatformPricing.value.ruleType,
      },
      season_insurance: form.season_insurance,
      stamina_level: form.stamina_level,
      load_level: form.load_level,
      resources: pricing.quantityItems.value
        .map(item => ({
          key: item.key,
          label: item.label,
          price: item.price,
          quantity: pricing.isQuantityItemDisabled(item)
            ? 0
            : Number(quantityValues[item.key] || 0),
          mode: pricing.isQuantityItemDisabled(item) ? '赠送' : quantityModes[item.key] || '收费',
        }))
        .filter(item => item.quantity > 0),
      skin_groups: pricing.skinGroups.value.reduce<Record<string, string[]>>((groups, group) => {
        groups[group.key] = group.options.filter(skin => selectedSkins.value.includes(skin))
        return groups
      }, {}),
      screenshot_groups: buildScreenshotGroups(),
      online_time: {
        start: form.online_start,
        end: form.online_end,
      },
      unlock_said: form.unlock_said,
      ban_record: form.ban_record,
      common_regions: form.common_regions,
      remark: form.remark,
    }
  }

  function buildScreenshotGroups() {
    return pricing.screenshotSlots.value.reduce<Record<string, string[]>>((groups, slot) => {
      groups[slot.key] = getScreenshotURLs(slot.key)
      return groups
    }, {})
  }

  function getScreenshotURLs(key: ScreenshotKey) {
    return (screenshotFiles[key] || []).filter(Boolean)
  }

  function getScreenshotCount(key: ScreenshotKey) {
    return getScreenshotURLs(key).length
  }

  function getScreenshotLimit(key: ScreenshotKey) {
    return multiScreenshotKeys.has(key) ? multiScreenshotLimit : 1
  }

  function isMultiScreenshotSlot(key: ScreenshotKey) {
    return getScreenshotLimit(key) > 1
  }

  function canAddScreenshot(key: ScreenshotKey) {
    return !uploading.value && getScreenshotCount(key) < getScreenshotLimit(key)
  }

  function getScreenshotLimitHint(key: ScreenshotKey) {
    const limit = getScreenshotLimit(key)
    if (limit <= 1) return ''
    return `最多${limit}张，已上传${getScreenshotCount(key)}张`
  }

  function getScreenshotLabel(key: ScreenshotKey) {
    return pricing.screenshotSlots.value.find(item => item.key === key)?.label || '该截图'
  }

  function recordValue(value: unknown): Record<string, unknown> {
    return typeof value === 'object' && value !== null && !Array.isArray(value)
      ? (value as Record<string, unknown>)
      : {}
  }

  function arrayValue(value: unknown): unknown[] {
    return Array.isArray(value) ? value : []
  }

  function stringValue(value: unknown) {
    if (value === undefined || value === null) return ''
    return String(value)
  }

  function stringArray(value: unknown) {
    return Array.isArray(value)
      ? value.filter((item): item is string => typeof item === 'string' && Boolean(item))
      : []
  }

  function numberOrEmpty(value: unknown): number | '' {
    const parsed = Number(value)
    return Number.isFinite(parsed) && parsed > 0 ? parsed : ''
  }

  function numberOrDefault(value: unknown, fallback: number) {
    const parsed = Number(value)
    return Number.isFinite(parsed) ? parsed : fallback
  }

  return {
    ...pricing,
    dailyLossOptions,
    commonOnlineTimes,
    formatNumber,
    router,
    loading,
    uploading,
    publishAgreements,
    virtualAssetSaleAgreementChecked,
    sellerAgreementChecked,
    canPublishAfterAgreements,
    fileInput,
    activeUploadKey,
    form,
    quantityValues,
    quantityModes,
    screenshotFiles,
    screenshotPreviews,
    selectedSkins,
    uploadedScreenshotCount,
    requiredScreenshotCount,
    isEditMode,
    pageTitle,
    submitButtonText,
    submitLoadingText,
    disabledOnlineStartOptions,
    disabledOnlineEndOptions,
    loadPublishOptions,
    saveDraft,
    handleSaveDraft,
    resetDraftState,
    handleResetDraft,
    isAllDayOnline,
    isOnlineStartPresetActive,
    isOnlineEndPresetActive,
    selectAllDayOnline,
    selectOnlineStart,
    selectOnlineEnd,
    handleOnlineStartChange,
    handleOnlineEndChange,
    toggleSkin,
    toggleRegion,
    triggerUpload,
    handleScreenshotUpload,
    removeScreenshot,
    getScreenshotURLs,
    getScreenshotCount,
    getScreenshotPreviewURL,
    getScreenshotLimit,
    isMultiScreenshotSlot,
    canAddScreenshot,
    getScreenshotLimitHint,
    handleFireLevelInput,
    handleAcceleratedSaleRatioInput,
    syncRecommendedDeposit,
    useRecommendedDeposit,
    handleDepositBlur,
    clearForbiddenQuantityItems,
    setQuantityMode,
    clampAcceleratedSaleRatioInput,
    useReferenceSaleRatio,
    useMaxAcceleratedSaleRatio,
    handleSubmit,
    validateForm,
    isScreenshotRequired,
    shouldRequireBanEvidence,
    buildAssetSummary,
  }
}

function parseOnlineTime(value: string) {
  const match = /^(\d{2}):(\d{2})$/.exec(value)
  if (!match) return null
  const hour = Number(match[1])
  const minute = Number(match[2])
  if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null
  return hour * 60 + minute
}

function normalizeOnlineTimeValue(value: string | number | null | undefined) {
  if (value === null || value === undefined) return ''
  return String(value)
}
