/**
 * Copyright (c) Nicolas Gallagher.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @noflow
 */

import StyleSheet from '../../StyleSheet/index'
import AccessibilityUtil from '../AccessibilityUtil/index'

const emptyObject = {}
const hasOwnProperty = Object.prototype.hasOwnProperty
const isArray = Array.isArray

const uppercasePattern = /[A-Z]/g
function toHyphenLower(match) {
  return '-' + match.toLowerCase()
}
function hyphenateString(str: string): string {
  return str.replace(uppercasePattern, toHyphenLower)
}
function processIDRefList(idRefList: string | Array<string>): string {
  return isArray(idRefList) ? idRefList.join(' ') : idRefList
}

const pointerEventsStyles = StyleSheet.create({
  auto: {
    pointerEvents: 'auto',
  },
  'box-none': {
    pointerEvents: 'box-none',
  },
  'box-only': {
    pointerEvents: 'box-only',
  },
  none: {
    pointerEvents: 'none',
  },
})

const createDOMProps = (elementType, props, options?) => {
  if (!props) {
    props = emptyObject
  }

  const {
    accessibilityActiveDescendant,
    accessibilityAtomic,
    accessibilityAutoComplete,
    accessibilityBusy,
    accessibilityChecked,
    accessibilityColumnCount,
    accessibilityColumnIndex,
    accessibilityColumnSpan,
    accessibilityControls,
    accessibilityCurrent,
    accessibilityDescribedBy,
    accessibilityDetails,
    accessibilityDisabled,
    accessibilityErrorMessage,
    accessibilityExpanded,
    accessibilityFlowTo,
    accessibilityHasPopup,
    accessibilityHidden,
    accessibilityInvalid,
    accessibilityKeyShortcuts,
    accessibilityLabel,
    accessibilityLabelledBy,
    accessibilityLevel,
    accessibilityLiveRegion,
    accessibilityModal,
    accessibilityMultiline,
    accessibilityMultiSelectable,
    accessibilityOrientation,
    accessibilityOwns,
    accessibilityPlaceholder,
    accessibilityPosInSet,
    accessibilityPressed,
    accessibilityReadOnly,
    accessibilityRequired,
    /* eslint-disable */
    accessibilityRole,
    /* eslint-enable */
    accessibilityRoleDescription,
    accessibilityRowCount,
    accessibilityRowIndex,
    accessibilityRowSpan,
    accessibilitySelected,
    accessibilitySetSize,
    accessibilitySort,
    accessibilityValueMax,
    accessibilityValueMin,
    accessibilityValueNow,
    accessibilityValueText,
    dataSet,
    focusable,
    nativeID,
    pointerEvents,
    style,
    testID,
    id,
    // Rest
    ...domProps
  } = props

  const disabled = accessibilityDisabled

  const role = AccessibilityUtil.propsToAriaRole(props)

  // ACCESSIBILITY
  if (accessibilityActiveDescendant != null) {
    domProps['aria-activedescendant'] = accessibilityActiveDescendant
  }
  if (accessibilityAtomic != null) {
    domProps['aria-atomic'] = accessibilityAtomic
  }
  if (accessibilityAutoComplete != null) {
    domProps['aria-autocomplete'] = accessibilityAutoComplete
  }
  if (accessibilityBusy != null) {
    domProps['aria-busy'] = accessibilityBusy
  }
  if (accessibilityChecked != null) {
    domProps['aria-checked'] = accessibilityChecked
  }
  if (accessibilityColumnCount != null) {
    domProps['aria-colcount'] = accessibilityColumnCount
  }
  if (accessibilityColumnIndex != null) {
    domProps['aria-colindex'] = accessibilityColumnIndex
  }
  if (accessibilityColumnSpan != null) {
    domProps['aria-colspan'] = accessibilityColumnSpan
  }
  if (accessibilityControls != null) {
    domProps['aria-controls'] = processIDRefList(accessibilityControls)
  }
  if (accessibilityCurrent != null) {
    domProps['aria-current'] = accessibilityCurrent
  }
  if (accessibilityDescribedBy != null) {
    domProps['aria-describedby'] = processIDRefList(accessibilityDescribedBy)
  }
  if (accessibilityDetails != null) {
    domProps['aria-details'] = accessibilityDetails
  }
  if (disabled === true) {
    domProps['aria-disabled'] = true
    // Enhance with native semantics
    if (
      elementType === 'button' ||
      elementType === 'form' ||
      elementType === 'input' ||
      elementType === 'select' ||
      elementType === 'textarea'
    ) {
      domProps.disabled = true
    }
  }
  if (accessibilityErrorMessage != null) {
    domProps['aria-errormessage'] = accessibilityErrorMessage
  }
  if (accessibilityExpanded != null) {
    domProps['aria-expanded'] = accessibilityExpanded
  }
  if (accessibilityFlowTo != null) {
    domProps['aria-flowto'] = processIDRefList(accessibilityFlowTo)
  }
  if (accessibilityHasPopup != null) {
    domProps['aria-haspopup'] = accessibilityHasPopup
  }
  if (accessibilityHidden === true) {
    domProps['aria-hidden'] = accessibilityHidden
  }
  if (accessibilityInvalid != null) {
    domProps['aria-invalid'] = accessibilityInvalid
  }
  if (accessibilityKeyShortcuts != null && Array.isArray(accessibilityKeyShortcuts)) {
    domProps['aria-keyshortcuts'] = accessibilityKeyShortcuts.join(' ')
  }
  if (accessibilityLabel != null) {
    domProps['aria-label'] = accessibilityLabel
  }
  if (accessibilityLabelledBy != null) {
    domProps['aria-labelledby'] = processIDRefList(accessibilityLabelledBy)
  }
  if (accessibilityLevel != null) {
    domProps['aria-level'] = accessibilityLevel
  }
  if (accessibilityLiveRegion != null) {
    domProps['aria-live'] =
      accessibilityLiveRegion === 'none' ? 'off' : accessibilityLiveRegion
  }
  if (accessibilityModal != null) {
    domProps['aria-modal'] = accessibilityModal
  }
  if (accessibilityMultiline != null) {
    domProps['aria-multiline'] = accessibilityMultiline
  }
  if (accessibilityMultiSelectable != null) {
    domProps['aria-multiselectable'] = accessibilityMultiSelectable
  }
  if (accessibilityOrientation != null) {
    domProps['aria-orientation'] = accessibilityOrientation
  }
  if (accessibilityOwns != null) {
    domProps['aria-owns'] = processIDRefList(accessibilityOwns)
  }
  if (accessibilityPlaceholder != null) {
    domProps['aria-placeholder'] = accessibilityPlaceholder
  }
  if (accessibilityPosInSet != null) {
    domProps['aria-posinset'] = accessibilityPosInSet
  }
  if (accessibilityPressed != null) {
    domProps['aria-pressed'] = accessibilityPressed
  }
  if (accessibilityReadOnly != null) {
    domProps['aria-readonly'] = accessibilityReadOnly
    // Enhance with native semantics
    if (
      elementType === 'input' ||
      elementType === 'select' ||
      elementType === 'textarea'
    ) {
      domProps.readOnly = true
    }
  }
  if (accessibilityRequired != null) {
    domProps['aria-required'] = accessibilityRequired
    // Enhance with native semantics
    if (
      elementType === 'input' ||
      elementType === 'select' ||
      elementType === 'textarea'
    ) {
      domProps.required = true
    }
  }
  if (role != null) {
    // 'presentation' synonym has wider browser support
    domProps['role'] = role === 'none' ? 'presentation' : role
  }
  if (accessibilityRoleDescription != null) {
    domProps['aria-roledescription'] = accessibilityRoleDescription
  }
  if (accessibilityRowCount != null) {
    domProps['aria-rowcount'] = accessibilityRowCount
  }
  if (accessibilityRowIndex != null) {
    domProps['aria-rowindex'] = accessibilityRowIndex
  }
  if (accessibilityRowSpan != null) {
    domProps['aria-rowspan'] = accessibilityRowSpan
  }
  if (accessibilitySelected != null) {
    domProps['aria-selected'] = accessibilitySelected
  }
  if (accessibilitySetSize != null) {
    domProps['aria-setsize'] = accessibilitySetSize
  }
  if (accessibilitySort != null) {
    domProps['aria-sort'] = accessibilitySort
  }
  if (accessibilityValueMax != null) {
    domProps['aria-valuemax'] = accessibilityValueMax
  }
  if (accessibilityValueMin != null) {
    domProps['aria-valuemin'] = accessibilityValueMin
  }
  if (accessibilityValueNow != null) {
    domProps['aria-valuenow'] = accessibilityValueNow
  }
  if (accessibilityValueText != null) {
    domProps['aria-valuetext'] = accessibilityValueText
  }

  // "dataSet" replaced with "data-*"
  const tmgCN = dataSet ? dataSet.className : undefined
  const tmgID = dataSet ? dataSet.id : undefined

  if (dataSet != null) {
    for (const dataProp in dataSet) {
      if (dataProp === 'className' || dataProp === 'id') continue
      if (hasOwnProperty.call(dataSet, dataProp)) {
        const dataName = hyphenateString(dataProp)
        const dataValue = dataSet[dataProp]
        if (dataValue != null) {
          domProps[`data-${dataName}`] = dataValue
        }
      }
    }
  }

  // FOCUS
  // "focusable" indicates that an element may be a keyboard tab-stop.
  if (focusable === false) {
    domProps.tabIndex = '-1'
  }
  if (
    // These native elements are keyboard focusable by default
    elementType === 'a' ||
    elementType === 'button' ||
    elementType === 'input' ||
    elementType === 'select' ||
    elementType === 'textarea'
  ) {
    if (focusable === false || accessibilityDisabled === true) {
      domProps.tabIndex = '-1'
    }
  } else if (
    // These roles are made keyboard focusable by default
    role === 'button' ||
    role === 'checkbox' ||
    role === 'link' ||
    role === 'radio' ||
    role === 'textbox' ||
    role === 'switch'
  ) {
    if (focusable !== false) {
      domProps.tabIndex = '0'
    }
  } else {
    // Everything else must explicitly set the prop
    if (focusable === true) {
      domProps.tabIndex = '0'
    }
  }

  // Resolve styles
  const [className, inlineStyle] = StyleSheet(
    [style, pointerEvents && pointerEventsStyles[pointerEvents]],
    { writingDirection: options ? options.writingDirection : 'ltr' }
  )
  if (className) {
    domProps.className = className
  }

  if (tmgCN) {
    domProps.className = tmgCN
  }

  if (inlineStyle) {
    domProps.style = inlineStyle
  }

  // OTHER
  // Native element ID
  const _id = tmgID || id || nativeID
  if (_id) {
    domProps.id = _id
  }
  // Automated test IDs
  if (testID != null) {
    domProps['data-testid'] = testID
  }

  return domProps
}

export default createDOMProps
