/**
 * respect https://github.com/cuth/postcss-pxtorem
 */
import unitless from '@emotion/unitless'

import type { CSSObject } from '..'
import type { Transformer } from './interface'

export interface Options {
  /**
   * The root font size.
   * @default 16
   */
  rootValue?: number
  /**
   * The decimal numbers to allow the REM units to grow to.
   * @default 5
   */
  precision?: number
  /**
   * Whether to allow px to be converted in media queries.
   * @default false
   */
  mediaQuery?: boolean
}

const pxRegex = /url\([^)]+\)|var\([^)]+\)|(\d+(?:\.\d+)?|\.\d+)px/g

function toFixed(number: number, precision: number) {
  const multiplier = 10 ** (precision + 1)
  const wholeNumber = Math.floor(number * multiplier)
  return (Math.round(wholeNumber / 10) * 10) / multiplier
}

const transform = (options: Options = {}): Transformer => {
  const { rootValue = 16, precision = 5, mediaQuery = false } = options

  const pxReplace = (m: string, $1: any) => {
    if (!$1) return m
    const pixels = Number.parseFloat($1)
    // covenant: pixels <= 1, not transform to rem @zombieJ
    if (pixels <= 1) return m
    const fixedVal = toFixed(pixels / rootValue, precision)
    return `${fixedVal}rem`
  }

  const visit = (cssObj: CSSObject): CSSObject => {
    const clone: CSSObject = { ...cssObj }

    Object.entries(cssObj).forEach(([key, value]) => {
      if (typeof value === 'string' && value.includes('px')) {
        const newValue = value.replace(pxRegex, pxReplace)
        clone[key] = newValue
      }

      // no unit
      if (!unitless[key] && typeof value === 'number' && value !== 0) {
        clone[key] = `${value}px`.replace(pxRegex, pxReplace)
      }

      // Media queries
      const mergedKey = key.trim()
      if (mergedKey.startsWith('@') && mergedKey.includes('px') && mediaQuery) {
        const newKey = key.replace(pxRegex, pxReplace)

        clone[newKey] = clone[key]
        delete clone[key]
      }
    })

    return clone
  }

  return { visit }
}

export default transform
