import Accounting from 'accounting'
import curry from 'ramda/src/curry'
import moment from 'wechat_common/lib/moment'

const format = {
  code: 'CNY',
  symbol: '¥',
  decimal: '.',
  thousand: '',
  precision: 2,
  name: 'Chinese Yuan',
}
const AMOUNT_UNIT = 100
const PHONE_REGEX = /^[+\d-\(\)]+$/
const MOBILE_REGEX = /^1[0-9]{10}$/
const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,10}(\s*;\s*[\w-\.]+@([\w-]+\.)+[\w-]{2,10})*(\s*;\s*)?$/

function checkMobile(mobile) {
  return MOBILE_REGEX.test(mobile)
}

function checkEmail(email) {
  return EMAIL_REGEX.test(email)
}

function getFormattedPrice(price) {
  return Accounting.formatMoney(price, format)
}

function getShippingFeeNum(cartData, settings) {
  const shippingRegionsData = settings.shippingRegions.default
    ? settings.shippingRegions.default
    : settings.shippingRegions.cn
  const { feePerOrder, feePerAdditionalItem } = shippingRegionsData

  const items = cartData.items

  if (!items.some(item => item.product.shippingInfo)) {
    return 0
  }

  const additionalFee = items.reduce((total, next) => {
    const fee = next.product.shippingInfo
      ? feePerAdditionalItem * next.orderItem.quantity
      : 0
    return total + fee
  }, 0)

  return feePerOrder - feePerAdditionalItem + additionalFee
}

function getTotalPriceNum(cartData) {
  let totalPrice = 0
  cartData.items.forEach(
    item => (totalPrice += item.orderItem.quantity * item.orderItem.price),
  )
  return totalPrice
}

function getTotalPriceNumWithoutFlashSale(cartData) {
  let totalPrice = 0
  cartData.items.forEach(item => {
    const canUseCoupon = !(
      item.product.flashSale && !item.product.flashSale.coupon
    )
    if (canUseCoupon) {
      totalPrice += item.orderItem.quantity * item.orderItem.price
    }
  })
  return totalPrice
}

function getDiscountNum(cartData, coupon, settings) {
  let number = 0

  if (coupon.category) {
    if (coupon.option.condition.productId) {
      const found = cartData.items.find(
        item =>
          Number(item.productId) === Number(coupon.option.condition.productId),
      )
      if (found) {
        const canUseCoupon = !(
          found.product.flashSale && !found.product.flashSale.coupon
        )
        if (canUseCoupon) {
          number =
            found.orderItem.price *
            found.orderItem.quantity *
            coupon.option.amount /
            100
        }
      }
    } else {
      switch (coupon.category) {
        case 'free_shipping':
          number = getShippingFeeNum(cartData, settings)
          return number
        case 'flat':
          number = coupon.option.amount
          break
        case 'percentage':
          number =
            getTotalPriceNumWithoutFlashSale(cartData) *
            coupon.option.amount /
            100
          break
        // no default
      }
    }
  }

  if (number >= getTotalPriceNum(cartData)) {
    number = getTotalPriceNum(cartData)
  }

  return number
}

function getPriceScope(prices) {
  if (!prices.length) {
    return '¥--'
  }

  let maxPrice = Math.max(...prices)
  let minPrice = Math.min(...prices)

  maxPrice =
    maxPrice % 1 === 0 ? maxPrice : getFormattedPrice(maxPrice).slice(1) // remove the currency symbol
  minPrice =
    minPrice % 1 === 0 ? minPrice : getFormattedPrice(minPrice).slice(1)

  if (maxPrice > minPrice) {
    return `¥${minPrice} - ${maxPrice}`
  }
  return `¥${minPrice}`
}

function getMinPrice(prices) {
  if (!prices.length) {
    return '¥--'
  }

  const minPrice = Math.min(...prices)
  if (minPrice >= 10000) {
    return `￥${Number(Math.floor(minPrice / 1000) / 10).toFixed(1)}万`
  } else {
    return minPrice % 1 === 0 ? `¥${minPrice}` : getFormattedPrice(minPrice)
  }
}

function getItemsNum(cartData) {
  let total = 0
  cartData.items.forEach(item => (total += item.orderItem.quantity))
  return total
}

function getPointsDiscountAmount(cartData) {
  if (!cartData.points) {
    return 0
  }
  const { isUsePointsDeductible, pointsDeductibleAmount } = cartData.points
  if (isUsePointsDeductible && pointsDeductibleAmount) {
    return pointsDeductibleAmount * AMOUNT_UNIT
  } else {
    return 0
  }
}

function getCouponBaseAmount(cartData, couponSaveMoney, settings) {
  let amount = 0
  amount = getTotalPriceNum(cartData) - couponSaveMoney
  if (amount < 0) {
    amount = 0
  }
  return amount
}

function getFinalAmount(shippingFeeBaseAmount) {
  let finalAmount = 0
  if (shippingFeeBaseAmount < 0) {
    finalAmount = 0
  } else {
    finalAmount = shippingFeeBaseAmount
  }
  return finalAmount / 100
}

function getPointsBaseAmount(
  shippingFeeBaseAmount,
  discountAmount,
  isOpenDeduct,
) {
  let pointsBaseAmount = 0
  const expandedDiscountAmount = discountAmount * AMOUNT_UNIT

  if (isOpenDeduct) {
    pointsBaseAmount = shippingFeeBaseAmount - expandedDiscountAmount
  } else {
    pointsBaseAmount = shippingFeeBaseAmount
  }
  if (pointsBaseAmount < 0) {
    pointsBaseAmount = 0
  }
  return pointsBaseAmount
}

function getShippingFeeBaseAmount(
  cartData,
  settings,
  couponBaseAmount,
  isNotNeedExpressFee,
) {
  let shippingFeeBaseAmount = 0
  const { coupon } = cartData
  const totalShippingFeeAmount = getShippingFeeNum(cartData, settings)

  if ((coupon && coupon.category === 'free_shipping') || isNotNeedExpressFee) {
    shippingFeeBaseAmount = couponBaseAmount
  } else {
    shippingFeeBaseAmount = couponBaseAmount + totalShippingFeeAmount
  }
  return shippingFeeBaseAmount
}

function canUseCoupon(cartData, coupon) {
  if (coupon.category) {
    if (coupon.option.condition.orderOver) {
      return getTotalPriceNum(cartData) >= coupon.option.condition.orderOver
    } else if (coupon.option.condition.productId) {
      return cartData.items.find(
        item =>
          Number(item.productId) === Number(coupon.option.condition.productId),
      )
    }
    return true
  }
}

function leftItemsNum(cartData, variation, product) {
  if (variation.quantity === -1) {
    return -1
  }

  const variationId = variation.id
  const foundItem = cartData.items.find(
    item => Number(item.orderItem.id) === Number(variationId),
  )

  let totalQuantity = variation.quantity

  // flash sale not started or expired, normal quantity should excludes flash quantity
  // make sure flash quantiy is enough
  const isFlashSaleStartedAndNotExpired =
    product &&
    product.flashSale &&
    !product.flashSale.started &&
    !product.flashSale.expired
  if (isFlashSaleStartedAndNotExpired) {
    const foundFlashVariation = product.flashSale.settings.find(
      item => item.lineItemId === variationId,
    )
    if (foundFlashVariation) {
      totalQuantity -= foundFlashVariation.flashQuantity
    }
  }
  // group buy not started or expired, normal quantity should excludes group buy quantity
  // make sure group buy quantiy is enough
  const isGroupBuyStartedAndNotExpired =
    product &&
    product.groupBuy &&
    !product.groupBuy.started &&
    !product.groupBuy.expired
  if (isGroupBuyStartedAndNotExpired) {
    const foundGroupBuyVariation = product.groupBuy.settings.find(
      item => item.lineItemId === variationId,
    )
    if (foundGroupBuyVariation) {
      totalQuantity -= foundGroupBuyVariation.groupBuyQuantity
    }
  }

  if (foundItem) {
    return totalQuantity - foundItem.orderItem.quantity
  }
  return totalQuantity
}

function selectedItemsNumForSameProduct(cartData, variation) {
  const { productId } = variation
  const foundItems =
    cartData.items.filter(
      item => Number(item.productId) === Number(productId),
    ) || []

  return foundItems.reduce((acc, next) => acc + next.orderItem.quantity, 0)
}

const sortWithOrderList = curry((orderList, data) => {
  data.sort((a, b) => {
    const aIndex = orderList[a.id] || -a.id
    const bIndex = orderList[b.id] || -b.id
    return aIndex - bIndex
  })
  return data
})

function sortWithOrder(orderList = {}) {
  return function(a, b) {
    const aIndex = orderList[a.id] || -a.id
    const bIndex = orderList[b.id] || -b.id
    return aIndex - bIndex
  }
}

function checkShippingInfo(data, key) {
  const shippingInfo = data.shippingInfo

  const errors = {}

  if (!shippingInfo.firstName) {
    errors.firstName = '姓名不能为空'
  }

  if (!PHONE_REGEX.test(shippingInfo.phone)) {
    errors.phone = '手机格式不正确'
  }

  if (shippingInfo.email && !EMAIL_REGEX.test(shippingInfo.email)) {
    errors.email = '邮箱格式不正确'
  }

  if (data.needShippingInfo) {
    const { provinceIndex, provinceArray } = data
    if (
      provinceArray[provinceIndex].name !== '海外' &&
      (data.provinceIndex === -1 || data.cityIndex === -1)
    ) {
      errors.district = '地区不能为空'
    }

    if (!shippingInfo.line1) {
      errors.line1 = '请填写详细地址'
    }
  }

  return !key ? errors[Object.keys(errors)[0]] : errors[key]
}

function getTime(date) {
  const time = new Date(date)
  return `${time.getFullYear()}年${time.getMonth() +
    1}月${time.getDate()}日 ${time.getHours()}:${time.getMinutes()}`
}

function formatProtocol(url) {
  if (!url) {
    return 'https://assets.sxlcdn.com/images/ecommerce/ecommerce-default-image.png'
  }
  const _url = url.replace('http:', '').replace('https:', '')
  return `https:${_url}`
}

function getNeedShippingInfo(products = []) {
  return products.some(product => product.shippingInfo)
}

function formatProductData(list, cartData) {
  list.forEach(product => {
    product.priceScope = getPriceScope(
      product.variations.map(variation => variation.price / 100),
    )
    product.outOfStock = !product.variations.some(
      variation => leftItemsNum(cartData, variation) !== 0,
    )
    if (product.picture.length > 0) {
      product.picture[0].thumbnailUrl = formatProtocol(
        product.picture[0].thumbnailUrl,
      )
    } else {
      product.picture = [
        {
          thumbnailUrl: formatProtocol(),
        },
      ]
    }
  })
  return list
}

function getCacheTime(duration) {
  return new Date().getTime() + duration
}

function formatFactory(method, key = 'url') {
  let assertError = ''
  if (typeof method !== 'function') {
    assertError = new TypeError(
      `method pass into formatFactory must be a function, but get ${typeof method}`,
    )
  }
  return function(obj) {
    if (assertError) {
      throw assertError
    }

    return Object.assign({}, obj, {
      [key]: method(obj[key]),
    })
  }
}

function caculateShortcuts(shortcuts) {
  let shortcutsFirstLine
  let shortcutsSecondLine
  let shortcutsSecondLineWrapperClass
  if (shortcuts && shortcuts.length > 5) {
    const center = Math.ceil(shortcuts.length / 2)
    shortcutsFirstLine = shortcuts.slice(0, center)
    shortcutsSecondLine = shortcuts.slice(center)

    const firstLineLength = shortcutsFirstLine.length
    const secondLineLength = shortcutsSecondLine.length
    if (firstLineLength !== secondLineLength) {
      if (secondLineLength === 3) {
        shortcutsSecondLineWrapperClass = 'shortcut-placeholder-junior'
      } else if (secondLineLength === 4) {
        shortcutsSecondLineWrapperClass = 'shortcut-placeholder-senior'
      }
    } else {
      shortcutsSecondLineWrapperClass = ' '
    }
  } else {
    shortcutsFirstLine = []
    shortcutsSecondLine = []
    shortcutsSecondLineWrapperClass = ' '
  }
  return [
    shortcutsFirstLine,
    shortcutsSecondLine,
    shortcutsSecondLineWrapperClass,
  ]
}

function caculateShortcutsGroup(shortcuts, type) {
  if (type === 'circle' || type === 'square') {
    if (shortcuts && shortcuts.length > 5) {
      const center = Math.ceil(shortcuts.length / 2)
      return [shortcuts.slice(0, center), shortcuts.slice(center)]
    }
  } else if (type === 'frame') {
    if (shortcuts) {
      if (shortcuts.length > 4 && shortcuts.length < 9) {
        const center = Math.ceil(shortcuts.length / 2)
        return [shortcuts.slice(0, center), shortcuts.slice(center)]
      } else if (shortcuts.length >= 9) {
        const middle = Math.ceil(shortcuts.length / 3)
        return [
          shortcuts.slice(0, middle),
          shortcuts.slice(middle, middle + 3),
          shortcuts.slice(middle + 3),
        ]
      }
    }
  }
  return []
}

const trimMoney = money => {
  if (money && typeof money === 'number' && !isNaN(money)) {
    const moneyString = `${money}`
    return Number(moneyString.substring(0, moneyString.length - 2))
  }
  return money
}

const trimMoneyWithPercentage = money => {
  if (money && typeof money === 'number' && !isNaN(money)) {
    return (100 - money) / 10
  }
  return money
}

const formatCouponForNewTemplate = coupon => {
  const { category } = coupon
  const preMappedCoupon = formatCouponDate(formatCoupon(coupon))

  // TODO: bad style
  return Object.assign({}, preMappedCoupon, {
    couponName: mapCouponName(category),
    condition: mapCondition(preMappedCoupon),
  })
}

const mapCouponName = category => {
  switch (category) {
    case 'flat':
      return '代金券'
    case 'free_shipping':
      return '包邮券'
    case 'percentage':
      return '折扣券'
    default:
      return '优惠券'
  }
}

const mapCondition = coupon => {
  if (coupon.option.condition.orderOver) {
    return `满${coupon.option.condition.orderOver}可用`
  } else if (coupon.option.condition.productId) {
    return '特定商品可用'
  } else {
    return '所有订单可用'
  }
}

const formatCoupon = coupon => {
  const { option, category } = coupon
  let couponName, conditionDescription, amount, sendMethod
  if (category === 'flat') {
    couponName = option.couponName || '抵扣券'
    amount = trimMoney(option.amount)
  } else if (category === 'free_shipping') {
    couponName = option.couponName || '免邮优惠'
    amount = '免邮'
  } else {
    couponName = option.couponName || '打折券'
    amount = trimMoneyWithPercentage(option.amount)
  }
  if (option.condition.orderOver) {
    conditionDescription = `消费满${trimMoney(
      option.condition.orderOver,
    )}元可使用`
  } else if (option.condition.productId) {
    conditionDescription = '特定商品可使用'
  } else if (option.condition.productCategoryId) {
    conditionDescription = '特定商品分类可使用'
  } else {
    conditionDescription = '适用所有商品'
  }

  const condition = Object.assign({}, option.condition, {
    orderOver: trimMoney(option.condition.orderOver),
    conditionDescription,
  })
  if (coupon.isReceivedWhenOrderPlaced) {
    sendMethod = 'isReceivedWhenOrderPlaced'
  } else if (coupon.isReceivedWhenBeingMember) {
    sendMethod = 'isReceivedWhenBeingMember'
  }
  const optionResult = Object.assign({}, option, {
    couponName,
    amount,
    condition,
  })

  return Object.assign({}, coupon, {
    option: optionResult,
    sendMethod,
  })
}

const formatDate = date => {
  if (!date) {
    return ''
  }

  return moment(date).format('YYYY.MM.DD')
}

const formatCouponDate = coupon => {
  const { startsAt, endsAt } = coupon
  const startFormat = formatDate(startsAt)
  const endFormat = formatDate(endsAt)
  return Object.assign({}, coupon, {
    startsAt: startFormat,
    endsAt: endFormat,
  })
}

const caculateSaveMoney = (currentShipping, couponId) => {
  let couponSaveMoney = 0
  let couponSaveMoneyToShow = ''
  const discountInfo = currentShipping.validCoupon[couponId]

  if (!currentShipping.optimalDiscountCoupon && !couponId) {
    couponSaveMoneyToShow = '无可用优惠券'
  } else if (discountInfo === 'free_shipping') {
    couponSaveMoneyToShow = '免邮'
  } else if (typeof discountInfo === 'number' && !isNaN(discountInfo)) {
    couponSaveMoneyToShow = `- ¥${discountInfo / 100}`
    couponSaveMoney = discountInfo
  } else {
    couponSaveMoneyToShow = '请手动选择优惠券'
  }

  return {
    couponSaveMoney,
    couponSaveMoneyToShow,
  }
}

const commonError = () => {
  wx.showModal({
    title: '网络错误',
    content: '请刷新重试',
  })
}

const ERROR_MSG = {
  expired: '优惠券已过期',
  'coupon not find': '优惠券未找到',
  'user not vip': '仅 VIP 用户可以领取',
  'has been used': '优惠券已被领取',
  'has been received': '优惠券仅可被兑换一次',
  'count not enough': '优惠券不足，无法领取',
  'not available to receive': '优惠券使用后方可继续兑换',
}

const showCouponStatus = state => {
  const { isFetching } = state.getIn(['coupon', 'user']).toJS()
  if (
    isFetching &&
    isFetching !== 'success' &&
    wx.showLoading &&
    wx.hideLoading
  ) {
    wx.showLoading()
  } else if (wx.hideLoading) {
    wx.hideLoading()
  }
}
const splitDate = date => {
  if (!date) {
    return ''
  }

  return new Date(date)
    .toLocaleString()
    .substr(0, 10)
    .split('/')
    .join('-')
}

const formatNumber = (number, precise) => Number(number).toFixed(precise)

const parseScene = scene => {
  const params = {}
  decodeURIComponent(scene)
    .split('&')
    .forEach((item, index) => {
      const itemArr = item.split('=')
      if (itemArr.length === 2) {
        params[itemArr[0]] = itemArr[1]
      } else {
        params[index] = itemArr[0]
      }
    })
  return params
}

const isEmptyDimension = dimension => {
  if (
    dimension === undefined ||
    dimension.name === '' ||
    dimension.options === undefined ||
    dimension.options === null ||
    dimension.options.length === 0
  ) {
    return true
  } else {
    return false
  }
}

const hasMutipleDimensions = dimensions => {
  if (!dimensions) {
    return false
  }
  const { dimension1, dimension2 } = dimensions
  return !isEmptyDimension(dimension1) && !isEmptyDimension(dimension2)
}

/**
 * according to the user's current consumption amount,
 * calculate how many points and points can be deducted.
 * Parameter:
 *   points: 2000
 *   confirmAmount: 600
 *   costBonusUnit: 100
 *   reduceMoney: 200
 *   max-reduce-amount => (2000 * 200 / 100)
 *
 * Method:
 *   calculatePointsDiscountData function
 *
 * conditions:
 * 1. the current confirm amount of the user is greater than
 *    the max deductible amount of the current points of the user
 *
 * 2. the current confirm amount of the user is less than
 *    the max deductible amount of the current points of the user
 *
 * Result:
 *   usePoints: the user can use the current consumption of points
 *   pointsDeductibleAmount: points can be used to offset the money
 *
 */

export function calculateCanbeUsedPoints(
  amount,
  costBonusUnit,
  reduceMoneyUnit,
) {
  return Math.ceil(amount * costBonusUnit / reduceMoneyUnit)
}

export function calculateMaxDeductibleAmount(
  totalPoints,
  reduceMoneyUnit,
  costBonusUnit,
) {
  return Math.floor(totalPoints * reduceMoneyUnit / costBonusUnit)
}

export function calculateCanbeDeductibleAmount(
  maxCanbeUsedPoints,
  costBonusUnit,
  reduceMoneyUnit,
) {
  return Math.ceil(maxCanbeUsedPoints * reduceMoneyUnit / costBonusUnit)
}

export function calculatePointsDiscountData(
  costBonusUnit,
  reduceMoney,
  costAmount,
  totalPoints,
) {
  let usePoints = 0
  let pointsDeductibleAmount = 0
  const maxDeductibleAmount = calculateMaxDeductibleAmount(
    totalPoints,
    reduceMoney,
    costBonusUnit,
  )

  if (maxDeductibleAmount - costAmount >= 0) {
    usePoints = calculateCanbeUsedPoints(costAmount, costBonusUnit, reduceMoney)
    pointsDeductibleAmount = costAmount
  } else {
    const maxCanbeUsedPoints = calculateCanbeUsedPoints(
      maxDeductibleAmount,
      costBonusUnit,
      reduceMoney,
    )
    if (totalPoints - maxCanbeUsedPoints >= 0) {
      pointsDeductibleAmount = calculateCanbeDeductibleAmount(
        maxCanbeUsedPoints,
        costBonusUnit,
        reduceMoney,
      )
      usePoints = maxCanbeUsedPoints
    } else {
      pointsDeductibleAmount = maxDeductibleAmount
      usePoints = calculateCanbeUsedPoints(
        maxDeductibleAmount,
        costBonusUnit,
        reduceMoney,
      )
    }
  }

  return {
    usePoints,
    expandedPointsDeductibleAmount: parseFloat(
      pointsDeductibleAmount / AMOUNT_UNIT,
    ).toFixed(2),
  }
}

export function getRestTime(time, interval, unit) {
  return moment(time)
    .add(interval, unit)
    .from(new Date())
}

export {
  checkMobile,
  checkEmail,
  getFormattedPrice,
  getPriceScope,
  getMinPrice,
  getItemsNum,
  getDiscountNum,
  getTotalPriceNum,
  getFinalAmount,
  getCouponBaseAmount,
  getPointsBaseAmount,
  getShippingFeeBaseAmount,
  getShippingFeeNum,
  canUseCoupon,
  leftItemsNum,
  selectedItemsNumForSameProduct,
  sortWithOrderList,
  checkShippingInfo,
  getTime,
  formatProtocol,
  getNeedShippingInfo,
  formatProductData,
  getCacheTime,
  formatFactory,
  sortWithOrder,
  caculateShortcuts,
  caculateShortcutsGroup,
  formatCoupon,
  formatCouponDate,
  formatCouponForNewTemplate,
  caculateSaveMoney,
  commonError,
  ERROR_MSG,
  splitDate,
  showCouponStatus,
  formatNumber,
  parseScene,
  isEmptyDimension,
  hasMutipleDimensions,
}
