// @flow
import {
  transformExpressionWithStyles,
  createTransformerMacro,
  getSourceMap,
  addImport
} from './utils'

export const transformCssCallExpression = ({
  state,
  babel,
  path,
  sourceMap,
  annotateAsPure = true
}: {
  state: *,
  babel: *,
  path: *,
  sourceMap?: string,
  annotateAsPure?: boolean
}) => {
  let node = transformExpressionWithStyles({
    babel,
    state,
    path,
    shouldLabel: true,
    sourceMap
  })
  if (node) {
    path.replaceWith(node)
    path.hoist()
  } else if (annotateAsPure && path.isCallExpression()) {
    path.addComment('leading', '#__PURE__')
  }
}

export const transformCsslessArrayExpression = ({
  state,
  babel,
  path
}: {
  babel: *,
  state: *,
  path: *
}) => {
  let t = babel.types
  let expressionPath = path.get('value.expression')
  let sourceMap =
    state.emotionSourceMap && path.node.loc !== undefined
      ? getSourceMap(path.node.loc.start, state)
      : ''

  expressionPath.replaceWith(
    t.callExpression(
      // the name of this identifier doesn't really matter at all
      // it'll never appear in generated code
      t.identifier('___shouldNeverAppearCSS'),
      path.node.value.expression.elements
    )
  )

  transformCssCallExpression({
    babel,
    state,
    path: expressionPath,
    sourceMap,
    annotateAsPure: false
  })

  if (t.isCallExpression(expressionPath)) {
    expressionPath.replaceWith(t.arrayExpression(expressionPath.node.arguments))
  }
}

export const transformCsslessObjectExpression = ({
  state,
  babel,
  path,
  cssImport
}: {
  babel: *,
  state: *,
  path: *,
  cssImport: { importSource: string, cssExport: string }
}) => {
  let t = babel.types
  let expressionPath = path.get('value.expression')
  let sourceMap =
    state.emotionSourceMap && path.node.loc !== undefined
      ? getSourceMap(path.node.loc.start, state)
      : ''

  expressionPath.replaceWith(
    t.callExpression(
      // the name of this identifier doesn't really matter at all
      // it'll never appear in generated code
      t.identifier('___shouldNeverAppearCSS'),
      [path.node.value.expression]
    )
  )

  transformCssCallExpression({
    babel,
    state,
    path: expressionPath,
    sourceMap
  })

  if (t.isCallExpression(expressionPath)) {
    expressionPath
      .get('callee')
      .replaceWith(
        addImport(state, cssImport.importSource, cssImport.cssExport, 'css')
      )
  }
}

let cssTransformer = ({
  state,
  babel,
  reference
}: {
  state: any,
  babel: any,
  reference: any
}) => {
  transformCssCallExpression({ babel, state, path: reference.parentPath })
}

let globalTransformer = ({
  state,
  babel,
  reference,
  importSource,
  options
}: {
  state: any,
  babel: any,
  reference: any,
  importSource: string,
  options: { cssExport?: string }
}) => {
  const t = babel.types

  if (
    !t.isJSXIdentifier(reference.node) ||
    !t.isJSXOpeningElement(reference.parentPath.node)
  ) {
    return
  }

  const stylesPropPath = reference.parentPath
    .get('attributes')
    .find(p => t.isJSXAttribute(p.node) && p.node.name.name === 'styles')

  if (!stylesPropPath) {
    return
  }

  if (t.isJSXExpressionContainer(stylesPropPath.node.value)) {
    if (t.isArrayExpression(stylesPropPath.node.value.expression)) {
      transformCsslessArrayExpression({
        state,
        babel,
        path: stylesPropPath
      })
    } else if (t.isObjectExpression(stylesPropPath.node.value.expression)) {
      transformCsslessObjectExpression({
        state,
        babel,
        path: stylesPropPath,
        cssImport:
          options.cssExport !== undefined
            ? {
                importSource,
                cssExport: options.cssExport
              }
            : {
                importSource: '@emotion/react',
                cssExport: 'css'
              }
      })
    }
  }
}

export const transformers = {
  // this is an empty function because this transformer is never called
  // we don't run any transforms on `jsx` directly
  // instead we use it as a hint to enable css prop optimization
  jsx: () => {},
  css: cssTransformer,
  Global: globalTransformer
}

export default createTransformerMacro(transformers, {
  importSource: '@emotion/react'
})
