import React from 'react'

type TypeDependency = unknown[]
type TypeDependencyNames = string

let what_debug_changed = 0

let configuration = { active: true }

function setUseWhatChange({ active = true }: { active?: boolean } = {}) {
  configuration = { ...configuration, active }
}

/**
 * Taken random color logic from some stackoverflow answer
 */
function getRandomColor() {
  const letters = '0123456789ABCDEF'
  let color = '#'
  for (let i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)]
  }
  return color
}

/**
 *
 * Check whether the dependency item is an object. then
 */
const isObject = (t: unknown) => {
  return Object.prototype.toString.call(t) === '[object Object]'
}

function getPrintableInfo(dependencyItem: unknown) {
  /**
   * Printing the info into viewable format
   */
  if (isObject(dependencyItem) || Array.isArray(dependencyItem)) {
    let ans
    try {
      ans = JSON.stringify(dependencyItem, null, 2)
    } catch {
      ans = 'CIRCULAR JSON'
    }
    return ans
  }

  return dependencyItem
}

// const isDevelopment = process.env['NODE_ENV'] === 'development';

function useHotRefs<T>(value: T) {
  const fnRef = React.useRef(value)
  React.useEffect(() => {
    fnRef.current = value
  })

  return fnRef
}

/**
 * Custom hook to track changes in dependencies and log them for debugging purposes.
 *
 * @param {TypeDependency} [dependency] - The dependencies to track. Can be an array of any type.
 * @param {TypeDependencyNames} [dependencyNames] - Comma-separated string of dependency names for better readability in logs. Should match the order of dependencies.
 * @param {string} [suffix] - Optional suffix to append to log messages. Useful for identifying the source/name of the hook or function.
 * @param {string} [hookName] - The name of the hook to use, either 'useEffect' or 'useLayoutEffect'. Defaults to 'useEffect'.
 *
 * @typedef {any[]} TypeDependency - An array of dependencies to track.
 * @typedef {string} TypeDependencyNames - A comma-separated string of dependency names.
 *
 * @example
 * // Usage example
 * useWhatChanged([prop1, prop2], 'prop1,prop2', 'MyComponent', 'useEffect');
 */
function useWhatChanged(
  dependency?: TypeDependency,
  dependencyNames?: TypeDependencyNames,
  suffix?: string,
  hookName?: string,
) {
  // It's a fair assumption the hooks type will not change for a component during
  // its life time
  const hookNameFinal = React.useMemo(() => {
    if (hookName === 'useLayoutEffect') {
      return 'useLayoutEffect'
    }

    // if(hookName === "useEffect" || !hookName) {
    return 'useEffect'
    // }
  }, [hookName])
  // This ref is responsible for book keeping of the old value
  const dependencyRef = React.useRef(dependency)

  // For count bookkeeping , for easy debugging
  const whatChangedHookCountRef = React.useRef(1)

  // For assigning color for easy debugging
  const backgroundColorRef = React.useRef('')

  const isDependencyArr = Array.isArray(dependencyRef.current)

  React[hookNameFinal](() => {
    if (
      dependencyRef.current &&
      isDependencyArr
      // dependencyRef.current.length > 0
    ) {
      what_debug_changed++

      whatChangedHookCountRef.current = what_debug_changed
      backgroundColorRef.current = getRandomColor()
    }

    // const MyWindow: IWindow = window;
  }, [dependencyRef, isDependencyArr])

  function postConsole() {
    console.log('\n')
    console.log(
      `%c///// END SECTION/////`,
      `background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
      '\n',
    )
    console.log('\n')
    console.log('\n')
  }
  function logBanners({
    isFirstMount,
    suffixText,
    isBlankArrayAsDependency,
  }: {
    isFirstMount?: boolean
    suffixText?: string
    isBlankArrayAsDependency?: boolean
  }) {
    if (configuration.active) {
      console.log(
        `%c///// START SECTION /////`,
        `background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
        '\n',
      )
      console.log('\n')
      console.log(
        `%c ${whatChangedHookCountRef.current} ${suffix || ''}`,
        `background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
        '👇🏾',
        `${isFirstMount ? 'FIRST RUN' : 'UPDATES'}`,
        `${suffixText}`,
      )

      if (isBlankArrayAsDependency) {
        postConsole()
      }
    }
  }

  const longBannersRef = useHotRefs(logBanners)

  React[hookNameFinal](() => {
    if (!(dependencyRef.current && isDependencyArr)) {
      return
    }

    // if (dependencyRef.current.length === 0) {
    //   return;
    // }

    // More info, if needed by user
    const stringSplitted = dependencyNames ? dependencyNames.split(',') : null
    let changed = false
    const whatChanged = dependency
      ? dependency.reduce(
          (
            acc: Record<string, { 'Old Value': unknown; 'New Value': unknown }>,
            dep,
            index,
          ) => {
            if (dependencyRef.current && dep !== dependencyRef.current[index]) {
              const oldValue = dependencyRef.current[index]
              dependencyRef.current[index] = dep
              if (dependencyNames && stringSplitted) {
                changed = true
                acc[`"✅" ${stringSplitted[index]}`] = {
                  'Old Value': getPrintableInfo(oldValue),
                  'New Value': getPrintableInfo(dep),
                }
              } else {
                acc[`"✅" ${index}`] = {
                  'Old Value': getPrintableInfo(oldValue),
                  'New Value': getPrintableInfo(dep),
                }
              }

              return acc
            }
            if (dependencyNames && stringSplitted) {
              acc[`"⏺" ${stringSplitted[index]}`] = {
                'Old Value': getPrintableInfo(dep),
                'New Value': getPrintableInfo(dep),
              }
            } else {
              acc[`"⏺" ${index}`] = {
                'Old Value': getPrintableInfo(dep),
                'New Value': getPrintableInfo(dep),
              }
            }

            return acc
          },
          {},
        )
      : {}
    if (configuration.active) {
      const isBlankArrayAsDependency =
        whatChanged && Object.keys(whatChanged).length === 0 && isDependencyArr
      longBannersRef.current({
        isFirstMount: !changed,
        suffixText: isBlankArrayAsDependency
          ? ` 👉🏽 This will run only once on mount.`
          : ``,
        isBlankArrayAsDependency,
      })

      if (!isBlankArrayAsDependency) {
        console.table(whatChanged)
        postConsole()
      }
    }
  }, [
    ...(() => {
      if (dependency && isDependencyArr) {
        return dependency
      }
      return []
    })(),
    dependencyRef,
    longBannersRef,
    hookName,
  ])
}

export { useWhatChanged, setUseWhatChange }
