import type {
  ChargeMode,
  ListingPublishOptions,
  PublishDepositRecommendConfig,
  PublishOptionGroup,
  PublishQuantityItem,
  PublishRatioConfig,
  PublishSalePriceConfig,
} from '@/features/listings/api/listingOptions'
import type { DepositBreakdownItem, PublishForm, PublishPlatformPricing } from '@/types/publish'

export const dailyLossOptions = [10, 20, 30, 40, 50]
export const commonOnlineTimes = ['00:00', '08:00', '10:00', '12:00', '14:00', '18:00', '20:00', '22:00', '23:59']

/**
 * 将金额四舍五入到角精度（0.1元）
 * 统一全项目金额处理规范
 * @example roundMoney(12.34) -> 12.3
 * @example roundMoney(12.36) -> 12.4
 * @example roundMoney(12.35) -> 12.4
 */
export function roundMoney(value: number) {
  return Math.round(value * 10) / 10
}

/**
 * 将比例四舍五入到一位小数
 */
export function roundRatio(value: number) {
  return Math.round(value * 10) / 10
}

/**
 * 格式化数字为字符串（保留1位小数）
 */
export function formatNumber(value: number) {
  const rounded = Math.round(value * 10) / 10
  return rounded.toFixed(1)
}

export function readUnitPrice(priceText: string) {
  const normalized = priceText.replace(/，/g, ',').trim()
  const fractionMatch = normalized.match(/(\d+(?:\.\d+)?)\s*元\s*\/\s*(\d+(?:\.\d+)?)/)
  if (fractionMatch) {
    const amount = Number(fractionMatch[1])
    const count = Number(fractionMatch[2])
    return count > 0 ? amount / count : 0
  }
  const singleMatch = normalized.match(/(\d+(?:\.\d+)?)\s*元/)
  return singleMatch ? Number(singleMatch[1]) : 0
}

export function calculateDailyLossRatioAdjustment(dailyLossMAmount: number) {
  return Math.min(Math.max(Math.floor((dailyLossMAmount - 10) / 10), 0), 4)
}

export function isGridCardQuantityItem(item: { key: string; label: string }) {
  return item.key === 'gridCard9' || item.label.includes('9格体验卡')
}

export function isQuantityItemDisabledForInsurance(item: { key: string; label: string }, seasonInsurance: string) {
  return seasonInsurance === '3*3' && isGridCardQuantityItem(item)
}

export function calculateConsumablePrice(options: {
  quantityItems: PublishQuantityItem[]
  quantityValues: Record<string, number>
  quantityModes: Record<string, ChargeMode>
  seasonInsurance: string
}) {
  const total = options.quantityItems.reduce((sum, item) => {
    const quantity = Number(options.quantityValues[item.key] || 0)
    const mode = options.quantityModes[item.key] || '收费'
    if (isQuantityItemDisabledForInsurance(item, options.seasonInsurance)) return sum
    if (quantity <= 0 || mode !== '收费') return sum
    return sum + quantity * readUnitPrice(item.price)
  }, 0)
  return roundMoney(total)
}

export function calculateRecommendedDeposit(options: {
  depositRecommendConfig: PublishDepositRecommendConfig
  skinGroups: PublishOptionGroup[]
  selectedSkins: string[]
}) {
  const baseAmount = Number(options.depositRecommendConfig.base_amount || 0)
  const skinAmount = options.depositRecommendConfig.skin_group_rules.reduce((sum, rule) => {
    const group = options.skinGroups.find((item) => item.key === rule.group_key)
    if (!group) return sum
    const selectedCount = group.options.filter((skin) => options.selectedSkins.includes(skin)).length
    return sum + selectedCount * Number(rule.amount_per_item || 0)
  }, 0)
  return roundMoney(baseAmount + skinAmount)
}

export function buildDepositBreakdownItems(options: {
  depositRecommendConfig: PublishDepositRecommendConfig
  skinGroups: PublishOptionGroup[]
  selectedSkins: string[]
}): DepositBreakdownItem[] {
  const items: DepositBreakdownItem[] = [
    {
      label: '基础押金',
      amount: Number(options.depositRecommendConfig.base_amount || 0),
      count: 1,
    },
  ]
  for (const rule of options.depositRecommendConfig.skin_group_rules) {
    const group = options.skinGroups.find((item) => item.key === rule.group_key)
    if (!group) continue
    const count = group.options.filter((skin) => options.selectedSkins.includes(skin)).length
    if (count <= 0) continue
    items.push({
      label: rule.label,
      amount: Number(rule.amount_per_item || 0) * count,
      count,
    })
  }
  return items
}

export function calculateSellerReferenceRatio(options: {
  coinMAmount: number
  form: PublishForm
  ratioConfig: PublishRatioConfig
  skinGroups: PublishOptionGroup[]
  selectedSkins: string[]
  levelOptions: string[]
  dailyLossRatioAdjustment: number
}) {
  const { coinMAmount, form, ratioConfig } = options
  if (coinMAmount <= 0 || !form.season_insurance || !form.stamina_level || !form.load_level) return 0
  const baseRatio = getInsuranceBaseRatio(ratioConfig, form.season_insurance)
  if (baseRatio <= 0) return 0
  return (
    baseRatio +
    calculateConfigPenalty(ratioConfig, options) +
    getCoinCorrection(ratioConfig, coinMAmount) +
    options.dailyLossRatioAdjustment
  )
}

export function readFinalSaleRatio(defaultRatio: number, acceleratedSaleRatio: number | '', maxAcceleratedSaleRatio: number) {
  if (defaultRatio <= 0) return 0
  if (!hasAcceleratedSaleRatioInput(acceleratedSaleRatio)) return defaultRatio
  const ratio = Number(acceleratedSaleRatio)
  if (!Number.isFinite(ratio) || ratio <= 0) return defaultRatio
  return roundRatio(Math.min(Math.max(ratio, defaultRatio), maxAcceleratedSaleRatio))
}

export function hasAcceleratedSaleRatioInput(value: number | '') {
  return value !== '' && value !== null
}

export function calculatePlatformPricing(options: {
  coinMAmount: number
  coinWanAmount: number
  sellerRatio: number
  sellerCoinBasePrice: number
  sellerTotalPrice: number
  consumablePrice: number
  salePriceConfig: PublishSalePriceConfig
}): PublishPlatformPricing {
  if (options.sellerRatio <= 0 || options.sellerCoinBasePrice <= 0) return emptyPlatformPricing()
  const fixedRule = findSaleFixedMarkupRule(options.salePriceConfig, options.coinMAmount)
  if (fixedRule) {
    return buildPlatformPricing(
      roundMoney(options.sellerCoinBasePrice + Number(fixedRule.markup_amount || 0)),
      'fixed_markup',
      options,
    )
  }
  const ratioRule = findSaleRatioAdjustmentRule(options.salePriceConfig, options.coinMAmount)
  const ratioSubtract = ratioRule ? Number(ratioRule.ratio_subtract || 0) : 0
  const buyerRatio = options.sellerRatio - ratioSubtract
  if (buyerRatio > 0 && ratioRule) {
    return buildPlatformPricing(roundMoney(options.coinWanAmount / buyerRatio), 'ratio_subtract', options)
  }
  return buildPlatformPricing(options.sellerCoinBasePrice, 'none', options)
}

export function emptyPlatformPricing(): PublishPlatformPricing {
  return {
    buyerCoinBasePrice: 0,
    buyerTotalPrice: 0,
    buyerRatio: 0,
    platformMarkupAmount: 0,
    ruleType: 'none',
  }
}

function buildPlatformPricing(
  buyerCoinBasePrice: number,
  ruleType: string,
  options: {
    coinWanAmount: number
    sellerTotalPrice: number
    consumablePrice: number
  },
): PublishPlatformPricing {
  const buyerTotalPrice = roundMoney(buyerCoinBasePrice + options.consumablePrice)
  return {
    buyerCoinBasePrice,
    buyerTotalPrice,
    buyerRatio: calculateEffectiveRatio(options.coinWanAmount, buyerCoinBasePrice),
    platformMarkupAmount: roundMoney(buyerTotalPrice - options.sellerTotalPrice),
    ruleType,
  }
}

function findSaleFixedMarkupRule(config: PublishSalePriceConfig, coinMAmount: number) {
  return [...config.fixed_markup_rules]
    .sort((a, b) => a.min_m - b.min_m)
    .find((item, index, rules) => isCoinInSaleRange(item, index, rules, coinMAmount, { includeLastMax: true }))
}

function findSaleRatioAdjustmentRule(config: PublishSalePriceConfig, coinMAmount: number) {
  return [...config.ratio_adjustment_rules]
    .sort((a, b) => a.min_m - b.min_m)
    .find((item, index, rules) => isCoinInSaleRange(item, index, rules, coinMAmount, { excludeFirstMin: true }))
}

function isCoinInSaleRange(
  item: { min_m: number; max_m: number },
  index: number,
  rules: Array<{ min_m: number; max_m: number }>,
  coinMAmount: number,
  options: { includeLastMax?: boolean; excludeFirstMin?: boolean } = {},
) {
  const maxM = Number(item.max_m || 0)
  const minM = Number(item.min_m || 0)
  const minMatched = options.excludeFirstMin && index === 0 ? coinMAmount > minM : coinMAmount >= minM
  const isLastRule = index === rules.length - 1
  const maxMatched = maxM <= 0 || coinMAmount < maxM || (options.includeLastMax && isLastRule && coinMAmount <= maxM)
  return minMatched && maxMatched
}

function calculateEffectiveRatio(coinWanAmount: number, price: number) {
  if (price <= 0) return 0
  return roundRatio(coinWanAmount / price)
}

function getInsuranceBaseRatio(config: Pick<ListingPublishOptions['ratio_config'], 'insurance_base_ratios'>, insurance: string) {
  return config.insurance_base_ratios.find((item) => item.insurance === insurance)?.ratio || 0
}

function calculateConfigPenalty(
  config: Pick<ListingPublishOptions['ratio_config'], 'config_items'>,
  options: {
    form: PublishForm
    skinGroups: PublishOptionGroup[]
    selectedSkins: string[]
    levelOptions: string[]
  },
) {
  return config.config_items.reduce((sum, item) => {
    return isRatioConfigItemMatched(item, options) ? sum : sum + Number(item.missing_penalty || 0)
  }, 0)
}

function isRatioConfigItemMatched(
  item: { kind: string; group_key?: string },
  options: {
    form: PublishForm
    skinGroups: PublishOptionGroup[]
    selectedSkins: string[]
    levelOptions: string[]
  },
) {
  if (item.kind === 'skin_group') return hasSelectedSkinGroup(item.group_key || '', options)
  if (item.kind === 'max_stamina') return isMaxLevel(options.form.stamina_level, options.levelOptions)
  if (item.kind === 'max_load') return isMaxLevel(options.form.load_level, options.levelOptions)
  return false
}

function hasSelectedSkinGroup(
  groupKey: string,
  options: {
    skinGroups: PublishOptionGroup[]
    selectedSkins: string[]
  },
) {
  const group = options.skinGroups.find((item) => item.key === groupKey)
  if (!group) return false
  return group.options.some((skin) => options.selectedSkins.includes(skin))
}

function isMaxLevel(value: string, levelOptions: string[]) {
  const currentLevel = readLevelNumber(value)
  const maxLevel = Math.max(...levelOptions.map(readLevelNumber).filter(Boolean))
  if (currentLevel > 0 && maxLevel > 0) return currentLevel >= maxLevel
  return value === levelOptions[levelOptions.length - 1]
}

function readLevelNumber(value: string) {
  const match = value.match(/\d+/)
  return match ? Number(match[0]) : 0
}

function getCoinCorrection(config: Pick<ListingPublishOptions['ratio_config'], 'coin_corrections'>, coinM: number) {
  return [...config.coin_corrections].sort((a, b) => b.threshold_m - a.threshold_m).find((item) => coinM > item.threshold_m)?.correction || 0
}
