import _ from 'lodash'
import postcss from 'postcss'
import cloneNodes from '../util/cloneNodes'
import buildMediaQuery from '../util/buildMediaQuery'
import buildSelectorVariant from '../util/buildSelectorVariant'

function isLayer(node) {
  if (Array.isArray(node)) {
    return node.length === 1 && isLayer(node[0])
  }
  return node.type === 'atrule' && node.name === 'layer'
}

function layerNodes(nodes) {
  return isLayer(nodes) ? nodes[0].nodes : nodes
}

export default function (config) {
  return function (css) {
    // Wrap any `responsive` rules with a copy of their parent `layer` to
    // ensure the layer isn't lost when copying to the `screens` location.
    css.walkAtRules('layer', (layerAtRule) => {
      const layer = layerAtRule.params
      layerAtRule.walkAtRules('responsive', (responsiveAtRule) => {
        const nestedlayerAtRule = postcss.atRule({
          name: 'layer',
          params: layer,
        })
        nestedlayerAtRule.prepend(responsiveAtRule.nodes)
        responsiveAtRule.removeAll()
        responsiveAtRule.prepend(nestedlayerAtRule)
      })
    })

    const {
      theme: { screens },
      separator,
    } = config
    const responsiveRules = postcss.root()
    const finalRules = []

    css.walkAtRules('responsive', (atRule) => {
      const nodes = atRule.nodes
      responsiveRules.append(...cloneNodes(nodes))

      // If the parent is already a `layer` (this is true for anything coming from
      // a plugin, including core plugins) we don't want to create a double nested
      // layer, so only insert the layer children. If there is no parent layer,
      // preserve the layer information when inserting the nodes.
      if (isLayer(atRule.parent)) {
        atRule.before(layerNodes(nodes))
      } else {
        atRule.before(nodes)
      }
      atRule.remove()
    })

    _.keys(screens).forEach((screen) => {
      const mediaQuery = postcss.atRule({
        name: 'media',
        params: buildMediaQuery(screens[screen]),
      })

      mediaQuery.append(
        _.tap(responsiveRules.clone(), (clonedRoot) => {
          clonedRoot.walkRules((rule) => {
            rule.selectors = _.map(rule.selectors, (selector) =>
              buildSelectorVariant(selector, screen, separator, (message) => {
                throw rule.error(message)
              })
            )
          })
        })
      )

      finalRules.push(mediaQuery)
    })

    const hasScreenRules = finalRules.some((i) => i.nodes.length !== 0)

    css.walkAtRules('tailwind', (atRule) => {
      if (atRule.params !== 'screens') {
        return
      }

      if (hasScreenRules) {
        atRule.before(finalRules)
      }

      atRule.remove()
    })
  }
}
