// @flow
import type {
  ElementType,
  StatelessFunctionalComponent,
  AbstractComponent
} from 'react'
import isPropValid from '@emotion/is-prop-valid'

export type Interpolations = Array<any>

export type StyledElementType<Props> =
  | string
  | AbstractComponent<{ ...Props, className: string }, mixed>

export type StyledOptions = {
  label?: string,
  shouldForwardProp?: string => boolean,
  target?: string
}

export type StyledComponent<Props> = StatelessFunctionalComponent<Props> & {
  defaultProps: any,
  toString: () => string,
  withComponent: (
    nextTag: StyledElementType<Props>,
    nextOptions?: StyledOptions
  ) => StyledComponent<Props>
}

export type PrivateStyledComponent<Props> = StyledComponent<Props> & {
  __emotion_real: StyledComponent<Props>,
  __emotion_base: any,
  __emotion_styles: any,
  __emotion_forwardProp: any
}

const testOmitPropsOnStringTag = isPropValid
const testOmitPropsOnComponent = (key: string) => key !== 'theme'

export const getDefaultShouldForwardProp = (tag: ElementType) =>
  typeof tag === 'string' &&
  // 96 is one less than the char code
  // for "a" so this is checking that
  // it's a lowercase character
  tag.charCodeAt(0) > 96
    ? testOmitPropsOnStringTag
    : testOmitPropsOnComponent

export const composeShouldForwardProps = (
  tag: PrivateStyledComponent<any>,
  options: StyledOptions | void,
  isReal: boolean
) => {
  let shouldForwardProp
  if (options) {
    const optionsShouldForwardProp = options.shouldForwardProp
    shouldForwardProp =
      tag.__emotion_forwardProp && optionsShouldForwardProp
        ? (propName: string) =>
            tag.__emotion_forwardProp(propName) &&
            optionsShouldForwardProp(propName)
        : optionsShouldForwardProp
  }

  if (typeof shouldForwardProp !== 'function' && isReal) {
    shouldForwardProp = tag.__emotion_forwardProp
  }

  return shouldForwardProp
}

export type CreateStyledComponent = <Props>(
  ...args: Interpolations
) => StyledComponent<Props>

export type CreateStyled = {
  <Props>(
    tag: StyledElementType<Props>,
    options?: StyledOptions
  ): (...args: Interpolations) => StyledComponent<Props>,
  [key: string]: CreateStyledComponent,
  bind: () => CreateStyled
}
