import {Any} from '@sanity/client'
import {CachingStrategy} from '@shopify/hydrogen'
import {ClientConfig} from '@sanity/client'
import {ClientPerspective} from '@sanity/client'
import {ClientReturn} from '@sanity/client'
import {createDataAttribute} from '@sanity/core-loader/create-data-attribute'
import {EncodeDataAttributeFunction} from '@sanity/core-loader/encode-data-attribute'
import type {HTMLProps} from 'react'
import {HydrogenSession} from '@shopify/hydrogen'
import type {ImageUrlBuilder} from '@sanity/image-url'
import type {InitializedClientConfig} from '@sanity/client'
import {PropsWithChildren} from 'react'
import {QueryParams} from '@sanity/client'
import type {QueryResponseInitial} from '@sanity/react-loader'
import {QueryWithoutParams} from '@sanity/client'
import {ReactNode} from 'react'
import {ResponseQueryOptions} from '@sanity/client'
import {SanityClient} from '@sanity/client'
import type {SanityImageSource} from '@sanity/image-url'
import {Session} from 'react-router'
import {SessionStorage} from 'react-router'
import {SuspenseProps} from 'react'
import {useEncodeDataAttribute} from '@sanity/react-loader'
import {useQuery as useQuery_2} from '@sanity/react-loader'
import {UseQueryOptionsDefinedInitial} from '@sanity/react-loader'

export {createDataAttribute}

/**
 * @public
 */
export declare function createSanityContext(
  options: CreateSanityContextOptions,
): Promise<SanityContext>

declare type CreateSanityContextOptions = {
  request: Request
  cache?: Cache | undefined
  waitUntil?: WaitUntil | undefined
  /**
   * Sanity client or configuration to use.
   */
  client: SanityClient | ClientConfig
  /**
   * The default caching strategy to use for `loadQuery` subrequests.
   * @see https://shopify.dev/docs/custom-storefronts/hydrogen/caching#caching-strategies
   *
   * Defaults to `CacheLong`
   */
  defaultStrategy?: CachingStrategy | null
  /**
   * Configuration for enabling preview mode.
   */
  preview?: {
    token: string
    session: SanityPreviewSession | HydrogenSession
  }
}

/** Default Sanity API version with perspective stack support */
export declare const DEFAULT_API_VERSION = 'v2025-02-19'

/** Default Hydrogen caching strategy for Sanity queries */
export declare const DEFAULT_CACHE_STRATEGY: CachingStrategy

export {EncodeDataAttributeFunction}

declare type FetchOptions<T> = HydrogenResponseQueryOptions & {
  hydrogen?: {
    /**
     * The caching strategy to use for the subrequest.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/caching#caching-strategies
     */
    cache?: CachingStrategy
    /**
     * Optional debugging information to be displayed in the subrequest profiler.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/debugging/subrequest-profiler#how-to-provide-more-debug-information-for-a-request
     */
    debug?: {
      displayName: string
    }
    /**
     * Whether to cache the result of the query or not.
     * @defaultValue () => true
     */
    shouldCacheResult?: (value: QueryResponseInitial<T>) => boolean
  }
}

/**
 * Reads the `sanity-preview-perspective` URL search param and validates it.
 * Returns `undefined` if absent or invalid, so callers can fall back to the session.
 */
export declare function getPerspectiveFromUrl(url: URL | string): ClientPerspective | undefined

declare type HydrogenResponseQueryOptions = Omit<ResponseQueryOptions, 'next' | 'cache'> & {
  hydrogen?: 'hydrogen' extends keyof RequestInit_2 ? RequestInit_2['hydrogen'] : never
}

declare type LoadQueryOptions<T> = Pick<
  HydrogenResponseQueryOptions,
  'perspective' | 'hydrogen' | 'useCdn' | 'stega' | 'headers' | 'tag'
> & {
  hydrogen?: {
    /**
     * The caching strategy to use for the subrequest.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/caching#caching-strategies
     */
    cache?: CachingStrategy
    /**
     * Optional debugging information to be displayed in the subrequest profiler.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/debugging/subrequest-profiler#how-to-provide-more-debug-information-for-a-request
     */
    debug?: {
      displayName: string
    }
    /**
     * Whether to cache the result of the query or not.
     * @defaultValue () => true
     */
    shouldCacheResult?: (value: QueryResponseInitial<T>) => boolean
  }
}

declare interface PreviewSessionData {
  perspective: string
}

/**
 * Query component that provides live updates in preview mode and static data otherwise.
 *
 * @public
 */
export declare function Query<Result = Any, Query extends string = string>({
  query,
  params,
  options,
  children,
  ...suspenseProps
}: QueryProps<Result, Query> & Omit<SuspenseProps, 'children'>): ReactNode

declare interface QueryClientProps<Result = Any, Query extends string = string> {
  query: Query
  params?: QueryParams | QueryWithoutParams
  options: UseQueryOptionsDefinedInitial<ClientReturn<Query, Result>> &
    LoadQueryOptions<ClientReturn<Query, Result>>
  children: (
    data: ClientReturn<Query, Result>,
    encodeDataAttribute: EncodeDataAttributeFunction,
  ) => ReactNode
}

export declare interface QueryProps<Result = Any, Query extends string = string> extends Omit<
  QueryClientProps<Result, Query>,
  'options'
> {
  query: Query
  params?: QueryParams | QueryWithoutParams
  options: {
    initial: ClientReturn<Query, Result> | QueryResponseInitial<ClientReturn<Query, Result>>
  } & LoadQueryOptions<ClientReturn<Query, Result>>
  children: (
    data: ClientReturn<Query, Result>,
    encodeDataAttribute: EncodeDataAttributeFunction,
  ) => ReactNode
}

declare interface RequestInit_2 {
  hydrogen?: {
    /**
     * The caching strategy to use for the subrequest.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/caching#caching-strategies
     */
    cache?: CachingStrategy
    /**
     * Optional debugging information to be displayed in the subrequest profiler.
     * @see https://shopify.dev/docs/custom-storefronts/hydrogen/debugging/subrequest-profiler#how-to-provide-more-debug-information-for-a-request
     */
    debug?: {
      displayName: string
    }
  }
}

/**
 * Script component that hydrates Sanity configuration on the client side.
 * Injects provider configuration into the client bundle for SSR compatibility.
 */
export declare function Sanity(props: SanityProps): ReactNode

export declare interface SanityContext {
  /**
   * Query Sanity using the loader.
   * @see https://www.sanity.io/docs/loaders
   */
  loadQuery<Result = Any, Query extends string = string>(
    query: Query,
    params?: QueryParams | QueryWithoutParams,
    options?: LoadQueryOptions<ClientReturn<Query, Result>>,
  ): Promise<QueryResponseInitial<ClientReturn<Query, Result>>>
  /**
   * Query Sanity using direct client fetch with Hydrogen caching.
   * Use this when you need direct client results without react-loader integration.
   * Automatically disables caching in preview mode for real-time updates.
   */
  fetch<Result = Any, Query extends string = string>(
    query: Query,
    params?: QueryParams | QueryWithoutParams,
    options?: FetchOptions<Result>,
  ): Promise<ClientReturn<Query, Result>>
  /**
   * Conditionally query Sanity using either loadQuery (for preview mode) or fetch (for static mode).
   * This optimizes bundle size by only loading @sanity/react-loader dependencies when in preview mode.
   */
  query<Result = Any, Query extends string = string>(
    query: Query,
    params?: QueryParams | QueryWithoutParams,
    options?: LoadQueryOptions<ClientReturn<Query, Result>> & FetchOptions<Result>,
  ): Promise<QueryResponseInitial<ClientReturn<Query, Result>> | ClientReturn<Query, Result>>
  /**
   * The Sanity client, automatically configured for preview mode when enabled.
   * Uses preview token, perspective, and CDN settings based on session state.
   */
  client: SanityClient
  preview?: CreateSanityContextOptions['preview'] & {
    /**
     * Whether preview mode is currently enabled based on session detection
     */
    enabled: boolean
  }
  SanityProvider: (props: PropsWithChildren<object>) => ReactNode
}

/**
 * Interface for Sanity preview session management.
 */
declare interface SanityPreviewSession {
  has: Session<PreviewSessionData, never>['has']
  get: Session<PreviewSessionData, never>['get']
  set: Session<PreviewSessionData, never>['set']
  unset: Session<PreviewSessionData, never>['unset']
  commit: () => ReturnType<SessionStorage<PreviewSessionData, never>['commitSession']>
  destroy: () => ReturnType<SessionStorage<PreviewSessionData, never>['destroySession']>
}

/**
 * Props for the Sanity script component.
 * Extends HTMLScriptElement props while excluding conflicting attributes.
 */
declare type SanityProps = Omit<
  HTMLProps<HTMLScriptElement>,
  | 'children'
  | 'async'
  | 'defer'
  | 'src'
  | 'type'
  | 'noModule'
  | 'dangerouslySetInnerHTML'
  | 'suppressHydrationWarning'
>

/**
 * Contains essential Sanity client configuration and preview/stega state.
 */
declare interface SanityProviderValue extends Required<
  Pick<InitializedClientConfig, 'projectId' | 'dataset' | 'apiHost' | 'apiVersion' | 'perspective'>
> {
  previewEnabled: boolean
  stegaEnabled: boolean
}

export {useEncodeDataAttribute}

/**
 * Hook that generates image URLs from Sanity image assets.
 * Returns a configured image URL builder for the given source.
 */
export declare function useImageUrl(source: SanityImageSource): ImageUrlBuilder

/**
 * Hook that returns a Sanity image URL builder configured with current provider settings.
 * Use this to create custom image transformations beyond `useImageUrl`.
 */
export declare function useImageUrlBuilder(): ImageUrlBuilder

/**
 * Automatically registers with the query detection system.
 * This enables automatic live mode detection in `VisualEditing` components.
 */
export declare function useQuery<QueryResponseResult = unknown>(
  query: string,
  params?: Record<string, unknown>,
  options?: UseQueryOptionsDefinedInitial<QueryResponseResult>,
): ReturnType<typeof useQuery_2<QueryResponseResult>>

/**
 * Hook that retrieves the current Sanity provider configuration.
 * Must be used within a SanityProvider component tree.
 */
export declare function useSanityProviderValue(): SanityProviderValue

declare type WaitUntil = (promise: Promise<unknown>) => void

export * from '@sanity/react-loader'

export {}
