import React from 'react'
import PropTypes from 'prop-types'
import { validateThemeTokensVersion } from './utils/theme-tokens'
import { responsiveProps } from '../utils/props/responsiveProps'

export const uninitialisedError = new Error('Theme context used outside of ThemeProvider')

export const ThemeContext = React.createContext(uninitialisedError)
export const ThemeSetterContext = React.createContext(uninitialisedError)

// These options default to `true` in v1.x to match legacy defaults and avoid breaking changes.
// This should change in future major releases to become "opt-in" legacy support.
const defaultThemeOptions = {
  // TODO: switch `forceAbsoluteFontSizing` to be false by default in the next major version
  forceAbsoluteFontSizing: true,
  // TODO: switch `forceZIndex` to be false by default in the next major version
  forceZIndex: true,
  // TODO: switch `enableHelmetSSR` to be false by default in the next major version
  enableHelmetSSR: true,
  enableMediaQueryStyleSheet: false
}

export const ThemeProvider = React.forwardRef(
  // eslint-disable-next-line no-unused-vars
  ({ children, defaultTheme, themeOptions = {} }, _) => {
    const [theme, setTheme] = React.useState(defaultTheme)

    React.useEffect(() => {
      if (theme.metadata.name !== defaultTheme.metadata.name) {
        setTheme(defaultTheme)
      }
    }, [theme.metadata.name, defaultTheme])

    const appliedThemeOptions = { ...defaultThemeOptions, ...themeOptions }

    // Validate the theme tokens version on every render.
    // This will intentionally break the application when attempting to use an invalid theme.
    // This will surface an incompatibility quickly rather than allowing the potential for strange bugs due to missing or incompatible tokens.
    validateThemeTokensVersion(theme)

    return (
      <ThemeSetterContext.Provider value={setTheme}>
        <ThemeContext.Provider value={{ ...theme, themeOptions: appliedThemeOptions }}>
          {children}
        </ThemeContext.Provider>
      </ThemeSetterContext.Provider>
    )
  }
)

ThemeProvider.displayName = 'ThemeProvider'

ThemeProvider.propTypes = {
  /**
   * The subtree whose components resolve their tokens against `defaultTheme`, available via the
   * `useThemeTokens` hook.
   */
  children: PropTypes.node.isRequired,
  /**
   * Brand theme whose tokens are applied to the subtree, such as `@telus-uds/theme-allium`.
   */
  defaultTheme: PropTypes.shape({
    metadata: PropTypes.shape({
      themeTokensVersion: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired
    }).isRequired
  }).isRequired,
  /**
   * An object containing options allowing to customize the theming experience:
   *
   * - `forceAbsoluteFontSizing`: available on web only; when set to true, allows
   *   using absolute font sizing (in pixels, doesn't scale) instead of the
   *   relative sizing (in `rem`, scales depending on the browser settings)
   * - `contentMaxWidth`: allows configuration of the content max width to be used in components
   *   such as Footnote and Notification to avoid content to stretch width more then the page's width
   * - `forceZIndex`: available on web only, when set to false, sets zIndex on `View` to be `auto`
   *   and when true, sets zIndex to be `0` (the default from `react-native-web`)
   * - `enableHelmetSSR`: on Web SSR, allows React Helmet to run during server-side rendering. This should be
   *   disabled unless a web app has been specifically configured to stop React Helmet accumulating
   *   instances (which may cause a memory leak). See React Helmet's docs: https://github.com/nfl/react-helmet
   * - `enableMediaQueryStyleSheet`: enables the use of Media Query StyleSheet.
   */
  themeOptions: PropTypes.shape({
    forceAbsoluteFontSizing: PropTypes.bool,
    forceZIndex: PropTypes.bool,
    enableHelmetSSR: PropTypes.bool,
    contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number),
    enableMediaQueryStyleSheet: PropTypes.bool
  })
}
