/// <reference types="react" />

import {ComponentType} from 'react'
import {Context} from 'react'
import {FunctionComponent} from 'react'
import type * as React_2 from 'react'

/**
 * Base intent parameters
 *
 * @public
 * @todo dedupe with core/structure
 */
export declare interface BaseIntentParams {
  /**
   * Document schema type name to create/edit.
   * Required for `create` intents, optional for `edit` (but encouraged, safer and faster)
   */
  type?: string
  /**
   * ID of the document to create/edit.
   * Required for `edit` intents, optional for `create`.
   */
  id?: string
  template?: string
  /**
   * Experimental field path
   *
   * @beta
   * @experimental
   * @hidden
   */
  path?: string
  /**
   * Optional "mode" to use for edit intent.
   * Known modes are `structure` and `presentation`.
   */
  mode?: string
  /**
   * Arbitrary/custom parameters are generally discouraged - try to keep them to a minimum,
   * or use `payload` (arbitrary JSON-serializable object) instead.
   */
  [key: string]: string | undefined
}

/**
 * @internal
 * @param options - Route node options
 */
export declare function _createNode(options: RouteNodeOptions): Router

/**
 * Decode a path segment containing JSON parameters
 *
 * @param pathSegment - The path segment to decode
 * @returns The decoded parameters
 * @internal
 * @hidden
 */
export declare function decodeJsonParams(pathSegment?: string): Record<string, unknown>

/**
 * Encodes a set of parameters as a path segment, using base64url
 *
 * @param params - Paramters to encode
 * @returns The encoded parameters as a path segment
 * @internal
 * @hidden
 */
export declare function encodeJsonParams(params?: Record<string, unknown>): string

/**
 * Intent parameters (json)
 *
 * @public
 */
export declare type IntentJsonParams = {
  [key: string]: any
}

/**
 * @public
 *
 * @param props - Props to pass to `IntentLink` component.
 *  See {@link IntentLinkProps}
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *  return <IntentLink intent="edit" params={{id: 'abc123'}}>Edit</IntentLink>
 * }
 * ```
 */
export declare const IntentLink: React_2.ForwardRefExoticComponent<
  Omit<IntentLinkProps & React_2.HTMLProps<HTMLAnchorElement>, 'ref'> &
    React_2.RefAttributes<HTMLAnchorElement>
>

/**
 * Props for the {@link IntentLink} component.
 *
 * @public
 */
export declare interface IntentLinkProps {
  /**
   * The name of the intent.
   */
  intent: string
  /**
   * The parameters to include in the intent.
   * {@link IntentParameters}
   */
  params?: IntentParameters
  /**
   * Whether to replace the current URL in the browser history instead of adding a new entry.
   */
  replace?: boolean
}

/**
 * @public
 * @todo dedupe with intent types in core
 */
export declare type IntentParameters = BaseIntentParams | [BaseIntentParams, IntentJsonParams]

/** @internal */
export declare type InternalSearchParam = [scopedPath: string[], value: string]

/**
 * A component that creates an HTML anchor element.
 *
 * @public
 *
 * @param props - Props to pass to the `Link` component.
 *  See {@link LinkProps}
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *   return (
 *    <Link href="https://www.sanity.io" target="_blank" replace>
 *      Go to Sanity
 *    </Link>
 *   )
 * }
 * ```
 */
export declare const Link: React_2.ForwardRefExoticComponent<
  Omit<LinkProps & React_2.HTMLProps<HTMLAnchorElement>, 'ref'> &
    React_2.RefAttributes<HTMLAnchorElement>
>

/**
 * Props for the {@link Link} component.
 *
 * @public
 */
export declare interface LinkProps {
  /**
   * Whether to replace the current URL in the browser history instead of adding a new entry.
   */
  replace?: boolean
}

/** @internal */
export declare interface MatchError {
  type: 'error'
  node: RouterNode
  /**
   * Parameters found in the route string but not provided as a key in the state object
   */
  missingKeys: string[]
  /**
   * These are keys found in the state object but not in the route definition (and can't be mapped to a child route)
   */
  unmappableStateKeys: string[]
}

/** @internal */
export declare interface MatchOk {
  type: 'ok'
  node: RouterNode
  matchedState: Record<string, string>
  searchParams: InternalSearchParam[]
  child: MatchOk | undefined
}

/** @internal */
export declare type MatchResult = MatchError | MatchOk

/**
 * @public
 */
export declare interface NavigateOptions {
  /**
   * Indicates whether to replace the current state.
   */
  replace?: boolean
}

/**
 * @public
 */
export declare interface Route {
  /**
   * The raw string representation of the route.
   */
  raw: string
  /**
   * An array of route segments that make up the route.
   * See {@link RouteSegment}
   */
  segments: RouteSegment[]
  /**
   * An optional object containing route transforms.
   * See {@link RouteTransform} and {@link RouterState}
   */
  transform?: {
    [key: string]: RouteTransform<RouterState>
  }
}

/**
 * An object containing functions for creating routers and router scopes.
 * See {@link RouteObject}
 *
 * @public
 *
 * @example
 * ```ts
 * const router = route.create({
 *   path: "/foo",
 *   children: [
 *     route.create({
 *       path: "/bar",
 *       children: [
 *         route.create({
 *           path: "/:baz",
 *           transform: {
 *             baz: {
 *               toState: (id) => ({ id }),
 *               toPath: (state) => state.id,
 *             },
 *           },
 *         }),
 *       ],
 *     }),
 *   ],
 * });
 * ```
 */
export declare const route: RouteObject

/**
 * @public
 */
export declare type RouteChildren =
  | RouterNode[]
  | ((state: RouterState) => Router | RouterNode | RouterNode[] | undefined | false)

/**
 * @public
 */
export declare interface RouteNodeOptions {
  /**
   * The path of the route node.
   */
  path?: string
  /**
   * The children of the route node. See {@link RouteChildren}
   */
  children?: RouteChildren
  /**
   * The transforms to apply to the route node. See {@link RouteTransform}
   */
  transform?: {
    [key: string]: RouteTransform<any>
  }
  /**
   * The scope of the route node.
   */
  scope?: string
  /**
   * Optionally disable scoping of search params
   * Scoped search params will be represented as scope[param]=value in the url
   * Disabling this will still scope search params based on any parent scope unless the parent scope also has disabled search params scoping
   * Caution: enabling this can cause conflicts with multiple plugins defining search params with the same name
   */
  __unsafe_disableScopedSearchParams?: boolean
}

/**
 * Interface for the {@link route} object.
 *
 * @public
 */
export declare interface RouteObject {
  /**
   * Creates a new router.
   * Returns {@link Router}
   * See {@link RouteNodeOptions} and {@link RouteChildren}
   */
  create: (
    routeOrOpts: RouteNodeOptions | string,
    childrenOrOpts?: RouteNodeOptions | RouteChildren | null,
    children?: Router | RouteChildren,
  ) => Router
  /**
   * Creates a new router for handling intents.
   * Returns {@link Router}
   */
  intents: (base: string) => Router
  /**
   * Creates a new router scope.
   * Returns {@link Router}
   */
  scope(
    scopeName: string,
    routeOrOpts: RouteNodeOptions | string,
    childrenOrOpts?: RouteNodeOptions | RouteChildren | null,
    children?: Router | RouteChildren,
  ): Router
}

/**
 * @public
 */
export declare interface Router extends RouterNode {
  /**
   * Indicates whether this router is a route.
   * @internal
   */
  _isRoute: boolean
  /**
   * Encodes the specified router state into a path string.
   * See {@link RouterState}
   *
   */
  encode: (state: RouterState) => string
  /**
   * Decodes the specified path string into a router state.
   * See {@link RouterState}
   */
  decode: (path: string) => RouterState | null
  /**
   * Determines whether the specified path is not found.
   */
  isNotFound: (path: string) => boolean
  /**
   * Gets the base path of this router.
   */
  getBasePath: () => string
  /**
   * Gets the redirect base of this router.
   */
  getRedirectBase: (pathname: string) => string | null
  /**
   * Determines whether the specified path is the root path.
   */
  isRoot: (path: string) => boolean
}

/**
 * @internal
 */
export declare const RouterContext: Context<RouterContextValue | null>

/**
 * @public
 */
export declare interface RouterContextValue {
  /**
   * Resolves the path from the given router state. See {@link RouterState}
   */
  resolvePathFromState: (nextState: RouterState) => string
  /**
   * Resolves the intent link for the given intent name and parameters.
   * See {@link IntentParameters}
   */
  resolveIntentLink: (intentName: string, params?: IntentParameters) => string
  /**
   * Navigates to the given URL.
   * The function requires an object that has a path and an optional replace property.
   */
  navigateUrl: (opts: {path: string; replace?: boolean}) => void
  /**
   * Navigates to the given router state.
   * See {@link RouterState} and {@link NavigateOptions}
   */
  navigate: (nextState: RouterState, options?: NavigateOptions) => void
  /**
   * Navigates to the given intent.
   * See {@link RouterState} and {@link NavigateOptions}
   */
  navigateIntent: (intentName: string, params?: IntentParameters, options?: NavigateOptions) => void
  /**
   * The current router state. See {@link RouterState}
   */
  state: RouterState
}

/**
 * @public
 */
export declare interface RouterNode {
  /**
   * The route information for this node. See {@link Route}
   */
  route: Route
  /**
   * An optional scope for this node.
   */
  scope?: string
  /**
   * Optionally disable scoping of search params
   * Scoped search params will be represented as scope[param]=value in the url
   * Disabling this will still scope search params based on any parent scope unless the parent scope also has disabled search params scoping
   * Caution: enabling this can cause conflicts with multiple plugins defining search params with the same name
   */
  __unsafe_disableScopedSearchParams?: boolean
  /**
   * An optional object containing transforms to apply to this node.
   * See {@link RouteTransform} and {@link RouterState}
   */
  transform?: {
    [key: string]: RouteTransform<RouterState>
  }
  /**
   * The child nodes of this node. See {@link RouteChildren}
   */
  children: RouteChildren
}

/**
 * @example
 * ```tsx
 * import {
 *   NavigateOptions,
 *   route,
 *   RouterProvider,
 *   RouterState
 * } from 'sanity'
 * import {useCallback, useMemo} from 'react'
 *
 * function Root() {
 *   const router = useMemo(() => route.create('/'), [])
 *
 *   const [state, setState] = useState<RouterState>({})
 *
 *   const handleNavigate = useCallback((
 *     path: string,
 *     options?: NavigateOptions
 *   ) => {
 *     console.log('navigate', path, options)
 *
 *     setState(router.decode(path))
 *   }, [router])
 *
 *   return (
 *     <RouterProvider
 *       onNavigate={handleNavigate}
 *       router={router}
 *       state={state}
 *     >
 *       <div>This is a routed application</div>
 *     </RouterProvider>
 *   )
 * }
 * ```
 *
 * @param props - The component props.
 *  {@link RouterProviderProps}
 *
 * @public
 */
export declare function RouterProvider(props: RouterProviderProps): React_2.ReactElement

/**
 * The props for the {@link RouterProvider} component.
 *
 * @public
 */
export declare interface RouterProviderProps {
  /**
   * A function that is called when the user navigates to a new path.
   * Takes an object containing the path to navigate to and an optional `replace` flag.
   */
  onNavigate: (opts: {path: string; replace?: boolean}) => void
  /**
   * The router object that is used to handle navigation. See {@link Router}
   */
  router: Router
  /**
   * The current state of the router. See {@link RouterState}
   */
  state: RouterState
  /**
   * The child elements to render.
   */
  children: React_2.ReactNode
}

/**
 * @public
 */
export declare type RouterState = Record<string, unknown> & {
  _searchParams?: SearchParam[]
}

/**
 * A component that wraps a scoped router context, so that calls to
 * `useRouter()`, `useRouterState()`, and usage of `<StateLink />`
 * will be prefixed with the scope segment.
 *
 * @public
 *
 * @param props - Props to pass `RouteScope` component.
 *  See {@link RouteScopeProps}
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *  return (
 *    <RouteScope scope="foo">
 *      <StateLink state={{bar: 'baz'}}>Link</StateLink>
 *    </RouteScope>
 *  )
 * }
 * ```
 */
export declare function RouteScope(props: RouteScopeProps): React_2.ReactElement

/**
 * Props for the {@link RouteScope} component.
 *
 * @public
 */
export declare interface RouteScopeProps {
  /**
   * The scope for the nested routes.
   */
  scope: string
  /**
   * Optionally disable scoping of search params
   * Scoped search params will be represented as scope[param]=value in the url
   * Disabling this will still scope search params based on any parent scope unless the parent scope also has disabled search params scoping
   * Caution: enabling this can cause conflicts with multiple plugins defining search params with the same name
   */
  __unsafe_disableScopedSearchParams?: boolean
  /**
   * The content to display inside the route scope.
   */
  children: React_2.ReactNode
}

/**
 * @public
 */
export declare interface RouteSegment {
  /**
   * The name of the segment.
   */
  name: string
  /**
   * The type of the segment.
   * Can be either "dir" or "param".
   */
  type: 'dir' | 'param'
}

/**
 * @public
 */
export declare interface RouteTransform<T> {
  /**
   * Converts a path string to a state object.
   */
  toState: (value: string) => T
  /**
   * Converts a state object to a path string.
   */
  toPath: (value: T) => string
}

/**
 * @public
 */
export declare type SearchParam = [key: string, value: string]

/**
 * A component that creates a link that updates the URL state.
 *
 * @remarks
 * This component uses the {@link useStateLink} hook
 * to create a link that updates the URL state.
 *
 * @param props - Props to pass to the `StateLink` component.
 *  See {@link StateLinkProps}.
 *
 * @public
 *
 * @example
 * ```tsx
 * function MyComponent() {
 *  return <StateLink state={{foo: 'bar'}}>Link</StateLink>
 * }
 * ```
 */
export declare const StateLink: React_2.ForwardRefExoticComponent<
  Omit<StateLinkProps & Omit<React_2.HTMLProps<HTMLAnchorElement>, 'href'>, 'ref'> &
    React_2.RefAttributes<HTMLAnchorElement>
>

/**
 * Props for the {@link StateLink} component.
 *
 * @public
 */
export declare interface StateLinkProps {
  /**
   * Whether to replace the current history entry instead of adding a new one.
   */
  replace?: boolean
  /**
   * The state to associate with the link.
   */
  state?: Record<string, unknown>
  /**
   * Whether to navigate to the index page of the app.
   */
  toIndex?: boolean
}

/**
 *
 * Returns props for an anchor element that will trigger an intent when clicked.
 *
 * @example
 * ```tsx
 * const {onClick, href} = useIntentLink({
 *   intent: 'edit',
 *   params: {id: 'foo'}
 * })
 *
 * <a href={href} onClick={onClick}>Link to "foo" editor</a>
 * ```
 *
 * @public
 *
 * @param options - Options to use for the link
 *  {@link UseIntentLinkOptions}
 *
 * @returns - An object with `onClick` and `href` props to use for the link
 */
export declare function useIntentLink(options: UseIntentLinkOptions): {
  onClick: React.MouseEventHandler<HTMLElement>
  href: string
}

/**
 * @public
 */
export declare interface UseIntentLinkOptions {
  /**
   * The name of the intent to trigger.
   */
  intent: string
  /**
   * An optional click event handler.
   */
  onClick?: React.MouseEventHandler<HTMLElement>
  /**
   * Optional parameters to pass to the intent. See {@link IntentParameters}
   */
  params?: IntentParameters
  /**
   * Whether to replace the current URL in the browser history.
   */
  replace?: boolean
  /**
   * The target window or frame to open the link in.
   */
  target?: string
}

/**
 * Returns an object with an `onClick` function that can be used as a click handler for a link.
 *
 * @public
 *
 * @param options - An object containing the properties for the link.
 *  See {@link UseLinkOptions}
 *
 * @returns An object with an `onClick` function.
 *
 * @example
 * ```tsx
 * const linkProps = useLink({
 *  href: 'https://www.sanity.io',
 *  target: '_blank'
 * })
 *
 * <a {...linkProps}>Link</a>
 * ```
 */
export declare function useLink(options: UseLinkOptions): {
  onClick: React.MouseEventHandler<HTMLElement>
}

/**
 * @public
 */
export declare interface UseLinkOptions {
  /**
   * The URL that the link should navigate to.
   */
  href?: string
  /**
   * The event handler function that should be called when the link is clicked.
   */
  onClick?: React.MouseEventHandler<HTMLElement>
  /**
   * Whether the link should replace the current URL in the browser history.
   */
  replace?: boolean
  /**
   * The target window or frame that the linked document will open in.
   */
  target?: string
}

/**
 * Returns the router context value.
 * @public
 *
 * @returns The router context value.
 *  {@link RouterContextValue}
 * @throws An error if the router context value is missing.
 *
 * @example
 * ```tsx
 * const router = useRouter()
 * ```
 */
export declare function useRouter(): RouterContextValue

/**
 * @public
 *
 * @param selector - A selector function that receives the router state and returns a value. See {@link RouterState}
 *
 * @returns The value returned by the selector function or RouterState. See {@link RouterState}
 *
 * @example
 * ```tsx
 * const {activeTool} = useRouterState(state => state.tool)
 * ```
 */
export declare function useRouterState<R = RouterState>(
  selector: (routerState: RouterState) => R,
): R

/**
 * @public
 *
 * @returns The router state. See {@link RouterState}
 *
 * @example
 * ```tsx
 * const routerState = useRouterState()
 * ```
 */
export declare function useRouterState(): RouterState

/**
 * @public
 *
 * @param options - Options to use for the link
 *  {@link UseStateLinkOptions}
 *
 * @returns - An object with `onClick` and `href` props to use for the link
 *
 * @example
 * ```tsx
 * const {onClick, href} = useStateLink({state: {foo: 'bar'}})
 * ```
 */
export declare function useStateLink(options: UseStateLinkOptions): {
  onClick: React_2.MouseEventHandler<HTMLElement>
  href: string
}

/**
 * @public
 */
export declare interface UseStateLinkOptions {
  /**
   * The click event handler for the link.
   */
  onClick?: React_2.MouseEventHandler<HTMLElement>
  /**
   * Whether to replace the current history entry instead of adding a new one.
   */
  replace?: boolean
  /**
   * The state object to update when the link is clicked.
   */
  state?: Record<string, unknown>
  /**
   * The target window or frame to open the linked document in.
   */
  target?: string
  /**
   * Whether to navigate to the index page of the linked document.
   */
  toIndex?: boolean
}

/**
 * A higher-order component that injects the router object into its child component.
 *
 * @internal
 * @deprecated - Use the `useRouter` hook instead.
 *
 * @returns The rendered component.
 *
 * @example
 * ```tsx
 * function MyComponent(props: {router: Router}) {
 *   const {location} = props.router
 *   const {pathname} = location
 *   return <p>The current path is: {pathname}</p>
 * }
 *
 * function App() {
 *   return (
 *     <Router>
 *       <WithRouter>
 *         {router => <MyComponent router={router} />}
 *       </WithRouter>
 *     </Router>
 *   )
 * }
 * ```
 */
export declare const WithRouter: React_2.FunctionComponent<Omit<WithRouterProps, 'router'>>

/**
 * A higher-order component that injects the `router` object from the `useRouter` hook
 * into the props of the wrapped component.
 *
 * @internal
 * @deprecated - Use the `useRouter` hook instead.
 *
 * @param Component - The component to wrap.
 *
 * @returns The wrapped component.
 *
 * @example
 * ```tsx
 * function MyComponent(props) {
 *  return <div>{props.router.state.myParam}</div>
 * }
 *
 * export default withRouter(MyComponent)
 * ```
 */
export declare function withRouter<
  Props extends {
    router: RouterContextValue
  },
>(Component: ComponentType<Props>): FunctionComponent<Omit<Props, 'router'>>

/**
 * @internal
 * @deprecated - Use the `useRouter` hook instead.
 */
export declare interface WithRouterProps {
  /**
   * The `router` object from the `useRouter` hook.
   *  {@link RouterContextValue}
   */
  router: RouterContextValue
  /**
   * A function that renders the wrapped component with the `router` object as a parameter.
   */
  children: (router: RouterContextValue) => React_2.ReactElement
}

export {}
