"use strict"

const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration")
const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty")
const report = require("../../utils/report")
const ruleMessages = require("../../utils/ruleMessages")
const validateOptions = require("../../utils/validateOptions")
const shorthandData = require("../../reference/shorthandData")
const valueParser = require("postcss-value-parser")
const postcss = require("postcss")

const ruleName = "shorthand-property-no-redundant-values"

const messages = ruleMessages(ruleName, {
  rejected: (unexpected, expected) => `Unexpected longhand value '${unexpected}' instead of '${expected}'`,
})

const shorthandableProperties = new Set(Object.keys(shorthandData))

const ignoredCharacters = [
  "+",
  "-",
  "*",
  "/",
  "(",
  ")",
  "$",
  "@",
  "--",
  "var(",
]

const ignoredShorthandProperties = new Set([
  "background",
  "font",
  "border",
  "border-top",
  "border-bottom",
  "border-left",
  "border-right",
  "list-style",
  "transition",
])

function isIgnoredCharacters(value) {
  return ignoredCharacters.some(char => value.indexOf(char) !== -1)
}

function canCondense(top, right) {
  const bottom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null
  const left = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null

  const lowerTop = top.toLowerCase()
  const lowerRight = right.toLowerCase()
  const lowerBottom = bottom && bottom.toLowerCase()
  const lowerLeft = left && left.toLowerCase()

  if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
    return [top]
  } else if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
    return [
      top,
      right,
    ]
  } else if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
    return [
      top,
      right,
      bottom,
    ]
  } else {
    return [
      top,
      right,
      bottom,
      left,
    ]
  }
}

function canCondenseToOneValue(top, right, bottom, left) {
  if (top !== right) {
    return false
  }

  return top === bottom && (bottom === left || !left) || !bottom && !left
}

function canCondenseToTwoValues(top, right, bottom, left) {
  return top === bottom && right === left || top === bottom && !left && top !== right
}

function canCondenseToThreeValues(top, right, bottom, left) {
  return right === left
}

const rule = function (actual) {
  return (root, result) => {
    const validOptions = validateOptions(result, ruleName, { actual })
    if (!validOptions) {
      return
    }

    root.walkDecls(decl => {
      if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop)) {
        return
      }

      const prop = decl.prop,
        value = decl.value

      const normalizedProp = postcss.vendor.unprefixed(prop.toLowerCase())

      // Ignore not shorthandable properties, and math operations
      if (isIgnoredCharacters(value) || !shorthandableProperties.has(normalizedProp) || ignoredShorthandProperties.has(normalizedProp)) {
        return
      }

      const valuesToShorthand = []

      valueParser(value).walk(valueNode => {
        if (valueNode.type !== "word") {
          return
        }

        valuesToShorthand.push(valueParser.stringify(valueNode))
      })

      if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4) {
        return
      }

      const shortestForm = canCondense.apply(undefined, valuesToShorthand)
      const shortestFormString = shortestForm.filter(value => {
        return value
      }).join(" ")
      const valuesFormString = valuesToShorthand.join(" ")

      if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) {
        return
      }

      report({
        message: messages.rejected(value, shortestFormString),
        node: decl,
        result,
        ruleName,
      })
    })
  }
}

rule.ruleName = ruleName
rule.messages = messages
module.exports = rule
