function generateDeclaration (property, value, important, position) {
  return {
    type: 'declaration',
    property,
    value: value + (important ? ' !important' : ''),
    position
  }
}

function clearImportant (value) {
  var newValue = value.replace(/\s*!important/g, '')
  return {
    value: newValue,
    important: value !== newValue
  }
}

function transition (declaration) {
  var CHUNK_REGEXP = /^([a-z-_]\S*)(\s+[\d.]+m?s)?(\s+[a-z-_]\S*)?(\s+[\d.]+m?s)?/
  var { value, important } = clearImportant(declaration.value)
  var values = value.replace(/(\d)\s*,\s*/g, '$1#').split(',')
  var position = declaration.position
  var result = []
  var map = {
    'transition-property': [],
    'transition-duration': [],
    'transition-timing-function': [],
    'transition-delay': []
  }
  if (values.length) {
    for (var i1 = 0; i1 < values.length; i1++) {
      var match = values[i1].trim().match(CHUNK_REGEXP)
      if (!match) {
        return []
      }
      map['transition-property'].push(match[1] || 'all')
      map['transition-duration'].push((match[2] || '0s').trim())
      map['transition-timing-function'].push((match[3] || 'ease').trim().replace(/#/g, ', '))
      map['transition-delay'].push((match[4] || '0s').trim())
    }
    for (var key in map) {
      var value = map[key]
      value = value.find(item => item !== value[0]) ? value.join(', ') : value[0]
      result.push(generateDeclaration(key, value, important, position))
    }
  }
  return result
}

function createParser (property) {
  return function (declaration) {
    var { value, important } = clearImportant(declaration.value)
    var position = declaration.position
    var splitResult = value.split(/\s+/)
    var result = []
    switch (splitResult.length) {
      case 1:
        splitResult.push(splitResult[0], splitResult[0], splitResult[0])
        break
      case 2:
        splitResult.push(splitResult[0], splitResult[1])
        break
      case 3:
        splitResult.push(splitResult[1])
        break
    }
    result.push(
      generateDeclaration(property + '-top', splitResult[0], important, position),
      generateDeclaration(property + '-right', splitResult[1], important, position),
      generateDeclaration(property + '-bottom', splitResult[2], important, position),
      generateDeclaration(property + '-left', splitResult[3], important, position)
    )
    return result
  }
}

var margin = createParser('margin')

var padding = createParser('padding')

function border (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var property = declaration.property
  var position = declaration.position
  var splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/)
  var result = [/^[\d\.]+\S*$/, /^(solid|dashed|dotted)$/, /\S+/].map(item => {
    var index = splitResult.findIndex(str => item.test(str))
    return index < 0 ? null : splitResult.splice(index, 1)[0]
  })
  if (splitResult.length) {
    return declaration
  }
  return [
    generateDeclaration(property + '-width', (result[0] || '0').trim(), important, position),
    generateDeclaration(property + '-style', (result[1] || 'solid').trim(), important, position),
    generateDeclaration(property + '-color', (result[2] || '#000000').trim(), important, position)
  ]
}

function borderProperty (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var position = declaration.position
  var property = declaration.property.split('-')[1]
  var splitResult = value.replace(/\s*,\s*/g, ',').split(/\s+/)
  var result = []
  switch (splitResult.length) {
    case 1:
      return declaration
    case 2:
      splitResult.push(splitResult[0], splitResult[1])
      break
    case 3:
      splitResult.push(splitResult[1])
      break
  }
  result.push(
    generateDeclaration('border-top-' + property, splitResult[0], important, position),
    generateDeclaration('border-right-' + property, splitResult[1], important, position),
    generateDeclaration('border-bottom-' + property, splitResult[2], important, position),
    generateDeclaration('border-left-' + property, splitResult[3], important, position)
  )
  return result
}

function borderRadius (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var position = declaration.position
  var splitResult = value.split(/\s+/)
  var result = []
  if (value.includes('/')) {
    return declaration
  }
  switch (splitResult.length) {
    case 1:
      return declaration
    case 2:
      splitResult.push(splitResult[0], splitResult[1])
      break
    case 3:
      splitResult.push(splitResult[1])
      break
  }
  result.push(
    generateDeclaration('border-top-left-radius', splitResult[0], important, position),
    generateDeclaration('border-top-right-radius', splitResult[1], important, position),
    generateDeclaration('border-bottom-right-radius', splitResult[2], important, position),
    generateDeclaration('border-bottom-left-radius', splitResult[3], important, position)
  )
  return result
}

function flexFlow (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var position = declaration.position
  var splitResult = value.split(/\s+/)
  var result = [/^(column|column-reverse|row|row-reverse)$/, /^(nowrap|wrap|wrap-reverse)$/].map(item => {
    var index = splitResult.findIndex(str => item.test(str))
    return index < 0 ? null : splitResult.splice(index, 1)[0]
  })
  if (splitResult.length) {
    return declaration
  }
  return [
    generateDeclaration('flex-direction', result[0] || 'column', important, position),
    generateDeclaration('flex-wrap', result[1] || 'nowrap', important, position)
  ]
}

function font (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var position = declaration.position
  var splitResult = value.replace(/,\s*/g, ',').replace(/\s*\/\s*/, '/').replace(/['"].+?['"]/g, str => str.replace(/\s+/g, '#')).split(/\s+/)
  var result = []
  var styleValues = ['normal', 'italic', 'oblique']
  result.push(generateDeclaration('font-style', styleValues[Math.max(0, styleValues.indexOf(splitResult[0]))], important, position))
  var weight = splitResult.slice(0, -2).find(str => /normal|bold|lighter|bolder|\d+/.test(str))
  if (weight) {
    result.push(generateDeclaration('font-weight', weight, important, position))
  }
  splitResult = splitResult.slice(-2)
  if (/[\d\.]+\S*(\/[\d\.]+\S*)?/.test(splitResult[0])) {
    var [size, height] = splitResult[0].split('/')
    result.push(
      generateDeclaration('font-size', size, important, position),
      generateDeclaration('line-height', height || 'normal', important, position),
      generateDeclaration('font-family', splitResult[1].replace(/#/g, ' '), important, position)
    )
    return result
  }
  return []
}

function background (declaration) {
  var { value, important } = clearImportant(declaration.value)
  var position = declaration.position
  if (/^#?\S+$/.test(value) || /^rgba?(.+)$/.test(value)) {
    return generateDeclaration('background-color', value, important, position)
  } else if (/^linear-gradient(.+)$/.test(value)) {
    return generateDeclaration('background-image', value, important, position)
  } else {
    return declaration
  }
}

var parserCollection = {
  transition,
  margin,
  padding,
  border,
  'border-top': border,
  'border-right': border,
  'border-bottom': border,
  'border-left': border,
  'border-style': borderProperty,
  'border-width': borderProperty,
  'border-color': borderProperty,
  'border-radius': borderRadius,
  'flex-flow': flexFlow,
  font,
  background
}

module.exports = function (declarations) {
  return declarations.reduce((result, declaration) => {
    var parser = parserCollection[declaration.property]
    if (parser) {
      return result.concat(parser(declaration))
    } else {
      result.push(declaration)
      return result
    }
  }, [])
}
