import { shallowRef, watch } from 'vue'

import type { MaybeComputedElementRef } from './unrefElement'
import { unrefElement } from './unrefElement'
import type { UseResizeObserverOptions } from './useResizeObserver'
import { useResizeObserver } from './useResizeObserver'

export interface ElementSize {
  width: number
  height: number
}

/**
 * Reactive size of an HTML element.
 *
 * @see https://vueuse.org/useElementSize
 * @param target
 * @param callback
 * @param options
 */
export function useElementSize(
  target: MaybeComputedElementRef,
  initialSize: ElementSize = { width: 0, height: 0 },
  options: UseResizeObserverOptions = {},
) {
  const { box = 'content-box' } = options
  const width = shallowRef(initialSize.width)
  const height = shallowRef(initialSize.height)

  useResizeObserver(
    target,
    ([entry]) => {
      const boxSize =
        box === 'border-box'
          ? entry.borderBoxSize
          : box === 'content-box'
            ? entry.contentBoxSize
            : entry.devicePixelContentBoxSize

      if (boxSize) {
        width.value = boxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0)
        height.value = boxSize.reduce((acc, { blockSize }) => acc + blockSize, 0)
      } else {
        // fallback
        width.value = entry.contentRect.width
        height.value = entry.contentRect.height
      }
    },
    options,
  )

  watch(
    () => unrefElement(target),
    (ele) => {
      width.value = ele ? initialSize.width : 0
      height.value = ele ? initialSize.height : 0
    },
  )

  return {
    width,
    height,
  }
}

export type UseElementSizeReturn = ReturnType<typeof useElementSize>
