import React, { ComponentPropsWithoutRef, ComponentPropsWithRef, ElementType } from 'react'

/** Type for a a no-op function stub. */
export type AnyFunction = (...args: any[]) => any

/** Type for any number without breaking autocompletion. */
export type AnyNumber = number & {}

/** Type for any string without breaking autocompletion. */
export type AnyString = string & {}

/** Type of change event defaulted to input element */
export type ChangeEvent<T = HTMLInputElement> = React.ChangeEvent<T>

/** Type of change event handler defaulted to input element */
export type ChangeEventHandler<T = HTMLInputElement> = React.ChangeEventHandler<T>

export type CreateContextOptions = {
  /** Message displayed when context is used outside of the provider */
  errorMessage?: string
  /** Accessing context outside of provider won't throw error */
  isOptional?: boolean
  /** Name of the context */
  name?: string
}

/** Return value of the createContext function. */
export type CreateContextReturn<T> = [React.Provider<T>, () => T]

/** Basic dictionary typing. */
export type Dict<T = any> = Record<string, T>

/** Classic application's environment names. */
export type Environment = 'Prod' | 'Stag' | 'Test'

/** Type of blur/focus event defaulted to any html element */
export type FocusEvent<T = HTMLElement> = React.FocusEvent<T>

/** Type of blur/focus event handler defaulted to any html element */
export type FocusEventHandler<T = HTMLElement> = React.FocusEventHandler<T>

/** Type of keyboard event defaulted to any html element */
export type KeyboardEvent<T = HTMLElement> = React.KeyboardEvent<T>

/** Type of keyboard event handler defaulted to any html element */
export type KeyboardEventHandler<T = HTMLElement> = React.KeyboardEventHandler<T>

/** Type of mouse event defaulted to any html element */
export type MouseEvent<T = HTMLElement> = React.MouseEvent<T>

/** Type of mouse event handler defaulted to any html element */
export type MouseEventHandler<T = HTMLElement> = React.MouseEventHandler<T>

/** Merges component/element base props and custom props */
export type PropsOf<C extends ElementType, Props = object> = RightJoinProps<ComponentPropsWithoutRef<C>, Props>

/** Merges component/element base props and custom props including ref */
export type RefPropsOf<C extends ElementType, Props = object> = RightJoinProps<ComponentPropsWithRef<C>, Props>

/** Render props of component's children. */
export type RenderProps<P> = React.ReactNode | ((props: P) => React.ReactNode)

/** Merges props definitions, overriding source keys */
export type RightJoinProps<SourceProps, OverrideProps> = Omit<SourceProps, keyof OverrideProps> & OverrideProps

export type SortOrder = 'asc' | 'desc' | null

export type Sort<T = string> = {
  key: T
  order: SortOrder
}
