import {
  DocumentSchema,
  SanityNode,
  SchemaArrayItem,
  SchemaArrayNode,
  SchemaBooleanNode,
  SchemaInlineNode,
  SchemaNode,
  SchemaNullNode,
  SchemaNumberNode,
  SchemaObjectField,
  SchemaObjectNode,
  SchemaStringNode,
  SchemaUnionNode,
  SchemaUnionNodeOptions,
  SchemaUnionOption,
  SchemaUnknownNode,
} from '@sanity/presentation-comlink'
import type {
  ComponentProps,
  ComponentType,
  FunctionComponent,
  HTMLAttributes,
  PropsWithChildren,
  ReactElement,
} from 'react'
import {HTMLProps} from 'react'

export declare function defineOverlayComponent<
  T extends OverlayComponent<Record<string, unknown>, any>,
>(
  component: T,
  props?: Omit<ComponentProps<T>, keyof OverlayComponentProps>,
): {
  component: T
  props: typeof props
}

export declare function defineOverlayComponents<T extends OverlayComponent>(
  resolver: OverlayComponentResolver<T>,
): typeof resolver

/**
 * @public
 * Define an overlay plugin with conditional options parameter.
 * Adds a guard that combines the user-provided guard with the plugin's guard.
 */
export declare function defineOverlayPlugin<
  O extends Record<string, unknown> = Record<string, never>,
>(pluginDefinitionFn: OverlayPluginDefineFn<O>): OverlayPluginUserFn<O>

export {DocumentSchema}

/**
 * An element that is safe to parse
 * @internal
 */
export declare type ElementNode = HTMLElement | SVGElement

/**
 * @public
 * Helper type to check if an object has required keys
 */
export declare type HasRequiredKeys<T> = Record<string, never> extends T ? false : true

/**
 * @public
 */
export declare type OverlayComponent<
  T extends Record<string, unknown> = Record<string, unknown>,
  P extends OverlayElementParent = OverlayElementParent,
> = ComponentType<OverlayComponentProps<P | undefined> & T>

/**
 * @public
 */
export declare interface OverlayComponentProps<
  P extends OverlayElementParent = OverlayElementParent,
> extends OverlayComponentResolverContext<P> {
  PointerEvents: FunctionComponent<PropsWithChildren<HTMLAttributes<HTMLDivElement>>>
}

/**
 * @public
 */
export declare type OverlayComponentResolver<
  T extends OverlayComponent = OverlayComponent<Record<string, unknown>, any>,
> = (context: OverlayComponentResolverContext) =>
  | T
  | {
      component: T
      props?: Record<string, unknown>
    }
  | Array<
      | T
      | {
          component: T
          props?: Record<string, unknown>
        }
    >
  | ReactElement
  | undefined
  | void

/**
 * @public
 */
export declare interface OverlayComponentResolverContext<
  P extends OverlayElementParent = OverlayElementParent,
> {
  /**
   * The resolved field's document schema type
   */
  document: DocumentSchema
  /**
   * The element node that the overlay is attached to
   */
  element: ElementNode
  /**
   * The element node that the Sanity node data is detected on
   */
  targetElement: ElementNode
  /**
   * The resolved field schema type
   */
  field: OverlayElementField
  /**
   * Whether the overlay is focused or not
   */
  focused: boolean
  /**
   * The Sanity node data that triggered the overlay
   */
  node: SanityNode
  /**
   * The resolved field's parent schema type
   */
  parent: P
  /**
   * A convience property, equal to `field.value.type`
   */
  type: string
}

export declare type OverlayElementField =
  | SchemaArrayItem
  | SchemaObjectField
  | SchemaUnionOption
  | undefined

export declare type OverlayElementParent =
  | DocumentSchema
  | SchemaNode
  | SchemaArrayItem
  | SchemaUnionOption
  | SchemaUnionNode
  | undefined

/**
 * @public
 */
export declare type OverlayPluginComponent<
  T extends Record<string, unknown> = Record<string, unknown>,
  P extends OverlayElementParent = OverlayElementParent,
> = ComponentType<OverlayComponentResolverContext<P | undefined> & T>

/**
 * @public
 */
export declare type OverlayPluginDefineFn<O extends Record<string, unknown>> = (
  options: O,
) => OverlayPluginDefinition

/** @public  */
export declare type OverlayPluginDefinition =
  | OverlayPluginExclusiveDefinition
  | OverlayPluginHudDefinition

/** @public  */
export declare interface OverlayPluginDefinitionBase {
  name: string
  title?: string
  icon?: ComponentType
  guard?: (context: OverlayComponentResolverContext) => boolean
}

/** @public  */
export declare interface OverlayPluginExclusiveDefinition extends OverlayPluginDefinitionBase {
  type: 'exclusive'
  component?: OverlayPluginComponent<
    Record<string, unknown> & {
      closeExclusiveView: () => void
    },
    any
  >
}

/** @public  */
export declare interface OverlayPluginHudDefinition extends OverlayPluginDefinitionBase {
  type: 'hud'
  component?: OverlayPluginComponent<Record<string, unknown>, any>
}

/**
 * @public
 */
export declare type OverlayPluginUserFn<O extends Record<string, unknown>> =
  HasRequiredKeys<O> extends true
    ? (config: OverlayPluginUserFnConfig<O>) => OverlayPluginDefinition
    : (config?: OverlayPluginUserFnConfig<O>) => OverlayPluginDefinition

/**
 * @public
 */
export declare type OverlayPluginUserFnConfig<O extends Record<string, unknown>> = {
  guard?: OverlayPluginDefinition['guard']
} & OverlayPluginUserFnConfigOptions<O>

/**
 * @public
 */
export declare type OverlayPluginUserFnConfigOptions<O extends Record<string, unknown>> =
  HasRequiredKeys<O> extends true
    ? {
        options: O
      }
    : {
        options?: O
      }

export declare const PointerEvents: FunctionComponent<
  PropsWithChildren<HTMLAttributes<HTMLDivElement>>
>

export {SanityNode}

export {SchemaArrayItem}

export {SchemaArrayNode}

export {SchemaBooleanNode}

export {SchemaInlineNode}

export {SchemaNode}

export {SchemaNullNode}

export {SchemaNumberNode}

export {SchemaObjectField}

export {SchemaObjectNode}

export {SchemaStringNode}

export {SchemaUnionNode}

export {SchemaUnionNodeOptions}

export {SchemaUnionOption}

export {SchemaUnknownNode}

export declare const UnionInsertMenuOverlay: OverlayComponent<
  {
    direction?: 'horizontal' | 'vertical'
    hoverAreaExtent?: HTMLProps<HTMLDivElement>['height' | 'width']
  },
  SchemaUnionNode<SchemaNode>
>

export {}
