import {AgentActionPath} from '@sanity/client/stega'
import {CurrentUser} from 'sanity'
import {DocumentFieldActionDivider} from 'sanity'
import {DocumentFieldActionGroup} from 'sanity'
import {DocumentFieldActionItem} from 'sanity'
import {JSX as JSX_2} from 'react/jsx-runtime'
import {ObjectSchemaType} from 'sanity'
import {Path} from 'sanity'
import {Plugin as Plugin_2} from 'sanity'
import {PortableTextBlock} from '@portabletext/types'
import {PortableTextMarkDefinition} from '@portabletext/types'
import {PortableTextSpan} from '@portabletext/types'
import {SanityClient} from 'sanity'
import type {SanityClient as SanityClient_2} from '@sanity/client'
import {SanityDocumentLike} from 'sanity'
import {SchemaType} from 'sanity'
import {SchemaType as SchemaType_2} from '@sanity/types'

declare interface AgentActionConditionalPath {
  path: AgentActionPath
  readOnly: boolean
  hidden: boolean
}

export declare const assist: Plugin_2<void | AssistPluginConfig>

export declare interface AssistConfig {
  /**
   * As of v3.0 Assist can write to date and datetime fields.
   *
   * If this function is omitted from config, the plugin will use the default timeZone and locale
   * in the browser:
   *
   * ```ts
   * const {timeZone, locale} = Intl.DateTimeFormat().resolvedOptions()
   * ```
   *
   * The function will be called any time an instruction runs.
   *
   * @see #LocaleSettings.locale
   * @see #LocaleSettings.timeZone
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#getcanonicalocales
   * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
   */
  localeSettings?: (context: LocaleSettingsContext) => LocaleSettings
  /**
   * The max depth for document paths AI Assist will write to.
   *
   * Depth is based on field path segments:
   * - `title` has depth 1
   * - `array[_key="no"].title` has depth 3
   *
   * Be careful not to set this too high in studios with recursive document schemas, as it could have
   * negative impact on performance.
   *
   * Depth will be counted from the field the instruction is run from. For example, if an instruction
   * is attached to depth 6, the count starts from there (at 0, not at 6).
   *
   * Default: 4
   */
  maxPathDepth?: number
  /**
   * Influences how much the output of an instruction will vary.
   *
   * Min: 0 – re-running an instruction will often produce the same outcomes
   * Max: 1 – re-running an instruction can produce wildly different outcomes
   *
   * This parameter applies to _all_ instructions in the studio.
   *
   * Prior to v3.0, this defaulted to 0
   *
   * Default: 0.3
   */
  temperature?: number
}

export declare type AssistFieldActionGroup = Omit<
  DocumentFieldActionGroup,
  'renderAsButton' | 'expanded' | 'children'
> & {
  /**
   * `children` can include undefined entries in the action array. These will be filtered out.
   * If the group has no defined children, the group will also be filtered out.
   */
  children: (AssistFieldActionNode | undefined)[]
}

export declare type AssistFieldActionItem = Omit<
  DocumentFieldActionItem,
  'renderAsButton' | 'selected' | 'onAction'
> & {
  onAction: () => void | Promise<void>
}

export declare type AssistFieldActionNode =
  | AssistFieldActionItem
  | AssistFieldActionGroup
  | DocumentFieldActionDivider

export declare interface AssistFieldActionProps {
  /**
   * `actionType` will be `document` for action invoked from the top right document action menu, and
   * `field` when invoked from a field action menu.
   */
  actionType: 'document' | 'field'
  /**
   * This is the id of the current document pane; it contains `drafts.`or `versions. prefix` ect depending on context.
   * Use this for `documentId` when calling any `client.agent.action`.
   *
   * It is generally recommended to call actions from the studio like this:
   * ```ts
   * await client.agent.action.generate({
   *   targetDocument: {
   *      operation: 'createIfNotExists',
   *      _id: props.documentIdForAction,
   *      _type: props.documentSchemaType.name,
   *      initialValues: props.getDocumentValue()
   *   },
   *   //...
   * })
   * ```
   */
  documentIdForAction: string
  /**
   * Schema type of the current document.
   * @see documentIdForAction
   */
  documentSchemaType: ObjectSchemaType
  /**
   * Returns the current document value.
   *
   * Prefer passing this function to your hooks instead of passing the document value directly to avoid unnecessary re-renders.
   * @see documentIdForAction
   */
  getDocumentValue: () => SanityDocumentLike
  /**
   * Returns the current readOnly and hidden state of all conditional members in the current document form.
   *
   * Intended to be passed to agent actions `conditionalPaths.paths`.
   */
  getConditionalPaths: () => AgentActionConditionalPath[]
  /**
   * `schemaId` for the current workspace.
   *
   * Note: the workspace schema has to be deployed using `sanity schema deploy` or `sanity deploy`.
   *
   * Use this for `schemaId` when calling any `client.agent.action`.
   *
   * It is generally recommended to call actions from the studio like this:
   * ```ts
   * await client.agent.action.generate({
   *   targetDocument: {
   *      operation: 'createIfNotExists',
   *      _id: props.documentIdForAction,
   *      _type: props.documentSchemaType.name,
   *      initialValues: props.getDocumentValue()
   *   },
   *   //...
   * })
   *
   * ```
   */
  schemaId: string
  /**
   * This is the schema type of the field the actions will be attached to (ie, schemaType for `path`)
   *
   * It can be used with agent actions using `target.path`, to scope the action to a specific field.
   *
   * It is generally recommended to call actions from the studio like this:
   * ```ts
   * await client.agent.action.generate({
   *   targetDocument: {
   *      operation: 'createIfNotExists',
   *      _id: props.documentIdForAction,
   *      _type: props.documentSchemaType.name,
   *      initialValues: props.getDocumentValue()
   *   },
   *   target: {
   *      path: props.path
   *   },
   * })
   * ```
   */
  path: AgentActionPath
  /**
   * This is the schema type of the field the actions will be attached to (ie, schemaType for `path`).
   *
   * Typically useful to dynamically return different actions based on the schema type of the field.
   *
   * ```ts
   * if(isObjectSchemaType(schemaType)) {
   *   return [
   *     defineAssistFieldAction({
   *       title: 'Fill the object fields',
   *       icon: RobotIcon,
   *       onAction: () => {
   *         //...
   *       }
   *     })
   *   ]
   * }
   * return useMemo(() => {
   *
   *
   * }, [])
   *
   * ```
   */
  schemaType: SchemaType_2
  /**
   * Schema type of the parent field or array item holding this field.
   *
   * This can be undefined if the action was unable to resolve the parent type is excluded from AI Assist.
   *
   * @see schemaType
   * @see documentSchemaType
   */
  parentSchemaType?: SchemaType_2
}

export declare interface AssistOptions {
  aiAssist?: {
    /** Set to true to disable assistance for this field or type */
    exclude?: boolean
    /**
     * Set to true to add translation field-action to the field.
     * Only has an effect in document types configured for document or field level translations.
     */
    translateAction?: boolean
  }
}

declare interface AssistPluginConfig {
  translate?: TranslationConfig
  /**
   * Config that affects all instructions
   */
  assist?: AssistConfig
  fieldActions?: {
    title?: string
    /**
     * The returned array can include `undefined` entries in the action array. These will be filtered out.
     */
    useFieldActions?: (props: AssistFieldActionProps) => (AssistFieldActionNode | undefined)[]
  }
  /**
   * @internal
   */
  __customApiClient?: (defaultClient: SanityClient_2) => SanityClient_2
  /**
   * @internal
   */
  __presets?: Record<string, AssistPreset>
}

declare interface AssistPreset {
  fields?: PresetField[]
}

declare interface ContextBlock {
  _type: typeof instructionContextTypeName
  reference?: {
    _type: 'reference'
    _ref?: string
  }
}

export declare const contextDocumentTypeName: 'assist.instruction.context'

/**
 *
 */
export declare interface CustomInput {
  /**
   * Id for the input
   */
  id: string
  /**
   * Title of the input field
   */
  title: string
  /**
   * Additional info that will be displayed over the input
   */
  description?: string
}

export declare type CustomInputResult = {
  /**
   * Identifies which custom input the `result`belongs to
   */
  input: CustomInput
  /**
   * The text provided by the user in the input
   */
  result: string
}

/**
 * Default implementation for plugin config `translate.field.translationOutputs`
 *
 * @see FieldTranslationConfig#translationOutputs
 */
export declare const defaultLanguageOutputs: TranslationOutputsFunction

export declare function defineAssistFieldAction(
  action: Omit<AssistFieldActionItem, 'type'>,
): AssistFieldActionItem

export declare function defineAssistFieldActionGroup(
  group: Omit<AssistFieldActionGroup, 'type'>,
): AssistFieldActionGroup

export declare function defineFieldActionDivider(): DocumentFieldActionDivider

export declare interface DocumentMember {
  schemaType: SchemaType
  path: Path
  name: string
  value: unknown
}

export declare interface DocumentTranslationConfig {
  /**
   * Path to language field in documents. Can be a hidden field.
   * For instance: 'config.language'
   *
   * For projects that use the `@sanity/document-internationalization` plugin,
   * this should be the same as `languageField` config for that plugin.
   *
   * Default: 'language'
   */
  languageField: string
  /**
   * `documentTypes` should be an array of strings where each entry must match a name from your document schemas.
   *
   * If defined, this property will add a translate instruction to these document types.
   * If undefined, the instruction will be added to all documents with aiAssistance enabled and a field matching `documentLanguageField` config.
   *
   * Documents with translation support will get a "Translate document>" instruction added.
   **/
  documentTypes?: string[]
}

declare interface FieldRef extends PortableTextMarkDefinition {
  _type: typeof fieldReferenceTypeName
  path?: string
}

declare const fieldReferenceTypeName: 'sanity.assist.instruction.fieldRef'

export declare interface FieldTranslationConfig {
  /**
   * `documentTypes` should be an array of strings where each entry must match a name from your document schemas.
   *
   * If defined, matching document will get a "Translate fields" instruction added.
   **/
  documentTypes?: string[]
  /**
   *
   * Used for display strings in the Studio, and to determine languages for field level translations
   *
   * If the studio is using the sanity-plugin-internationalized-array plugin, this
   * should be set to the same configuration.
   */
  languages: Language[] | LanguageCallback
  /**
   * API version for client passed to LanguageCallback for languages
   * https://www.sanity.io/docs/api-versioning
   * @defaultValue '2022-11-27'
   */
  apiVersion?: string
  /**
   * Specify fields that should be available in the languages callback:
   * ```tsx
   * {
   *   select: {
   *     markets: 'markets'
   *   },
   *   languages: (client, {markets}) =>
   *     client.fetch('*[_type == "language" && market in $markets]{id,title}', {markets})
   * }
   * ```
   *
   * If the studio is using the sanity-plugin-internationalized-array plugin, this
   * should be set to the same configuration.
   */
  selectLanguageParams?: Record<string, string>
  /**
   * `translationOutputs` is used when the "Translate fields" instruction is started by a Studio user.
   *
   *  It determines the relationships between document paths: Given a document path and a language, into which
   *  sibling paths should translations be output.
   *
   * `translationOutputs` is invoked once per path in the document (limited to a depth of 6), with the following:
   *
   *  * `documentMember` - the field or array item for a given path; contains the path and its schemaType,
   *  * `enclosingType` - the schema type of parent holding the member
   *  * `translateFromLanguageId` - the languageId for the language the user want to translate from
   *  * `translateToLanguageIds` - all languageIds the user can translate to
   *
   * The function should return a `TranslationOutput[]` array that contains all the paths where translations from
   * documentMember language (translateFromLanguageId) should be output.
   *
   * The function should return `undefined` for all documentMembers that should not be directly translated,
   * or are nested fields under a translated path.
   *
   * ## Default function
   *
   * The default function for `translationOutputs` is configured to be automatically compatible with sanity-plugin-internationalized-array
   * and object types prefixed with "locale".
   *
   * See <link to source for defaultTranslationOutputs> implementation details.
   *
   * ## Example
   * A document has the following document members:
   * * `{path: 'localeObject.en', schemaType: ObjectSchemaType}`
   * * `{path: 'localeObject.en.title', schemaType: StringSchemaType}`
   * * `{path: 'localeObject.de', schemaType: ObjectSchemaType}`,
   * * `{path: 'localeObject.de.title', schemaType: StringSchemaType}`
   *
   * `translationOutputs` for invoked with `translateFromLanguageId` `en`,
   * should only return [{id: 'de', outputPath: 'localeObject.de'}] for the `'localeObject.en'` path,
   * and undefined for all the other members.
   *
   * ### Example implementation
   * ```ts
   * function translationOutputs(member, enclosingType, translateFromLanguageId, translateToLanguageIds)
   *   if (enclosingType.jsonType === 'object' && enclosingType.name.startsWith('locale') && translateFromLanguageId === member.name) {
   *      return translateToLanguageIds.map((translateToId) => ({
   *         id: translateToId,
   *         outputPath: [...member.path.slice(0, -1), translateToId],
   *      }))
   *   }
   *   return undefined
   * }
   * ```
   *
   * @see #maxPathDepth
   **/
  translationOutputs?: TranslationOutputsFunction
  /**
   * The max depth for document paths AI Assist will translate.
   *
   * Depth is based on field path segments:
   * - `title` has depth 1
   * - `array[_key="no"].title` has depth 3
   *
   * Be careful not to set this too high in studios with recursive document schemas, as it could have
   * negative impact on performance.
   *
   * Default: 6
   */
  maxPathDepth?: number
}

export declare type GetUserInput = (args: {
  /**
   * Dialog title
   */
  title: string
  /**
   * One titled input per array item
   */
  inputs: CustomInput[]
}) => Promise<CustomInputResult[] | undefined>

declare type InlinePromptBlock = PortableTextSpan | FieldRef | UserInputBlock | ContextBlock

declare const instructionContextTypeName: 'sanity.assist.instruction.context'

/**
 * Returns true if the `schemaType` or any of its parent types (`schemaType.type`)` has `name` equal
 * to `typeName`.
 *
 * Useful for checking if `schemaType` is a type alias of `ìmage`, `code` or similar.
 */
export declare function isType(schemaType: SchemaType, typeName: string): boolean

export declare interface Language {
  id: string
  title?: string
}

export declare type LanguageCallback = (
  client: SanityClient,
  selectedLanguageParams: Record<string, unknown>,
) => Promise<Language[]>

export declare interface LocaleSettings {
  /**
   * A valid Unicode BCP 47 locale identifier used to interpret and format
   * natural language inputs and date output. Examples include "en-US", "fr-FR", or "ja-JP".
   *
   * This affects how phrases like "next Friday" or "in two weeks" are parsed,
   * and how resulting dates are presented (e.g., 12-hour vs 24-hour format).
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#getcanonicalocales
   */
  locale: string
  /**
   * A valid IANA time zone identifier used to resolve relative and absolute
   * date expressions to a specific point in time. Examples include
   * "America/New_York", "Europe/Paris", or "Asia/Tokyo".
   *
   * This ensures phrases like "tomorrow at 9am" are interpreted correctly
   * based on the user's local time.
   *
   * @see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
   */
  timeZone: string
}

export declare interface LocaleSettingsContext {
  user: CurrentUser
  defaultSettings: LocaleSettings
}

declare interface OutputFieldItem {
  _type: typeof outputFieldTypeName
  _key: string
  relativePath?: string
}

declare const outputFieldTypeName: 'sanity.assist.output.field'

declare interface OutputTypeItem {
  _type: typeof outputTypeTypeName
  _key: string
  type?: string
  relativePath?: string
}

declare const outputTypeTypeName: 'sanity.assist.output.type'

declare interface PresetField {
  path?: string
  instructions?: PresetInstruction[]
}

declare interface PresetInstruction {
  _key: string
  prompt?: PromptTextBlock[]
  title?: string
  /**
   * String key from `@sanity/icons` IconMap
   */
  icon?: string
  /**
   * Type/field filter
   */
  output?: (OutputFieldItem | OutputTypeItem)[]
}

declare type PromptTextBlock = Omit<
  PortableTextBlock<never, InlinePromptBlock, 'normal', never>,
  '_type'
> & {
  _type: 'block'
}

export declare function SchemaTypeTool(): JSX_2.Element

export declare type TranslateStyleguide =
  | string
  | ((context: TranslateStyleguideContext) => Promise<string>)

export declare interface TranslateStyleguideContext {
  documentId: string
  schemaType: ObjectSchemaType
  client: SanityClient
  /**
   * Only provided for field translations
   */
  translatePath?: Path
}

export declare interface TranslationConfig {
  /**
   * Config for document types with fields in multiple languages in the same document.
   */
  field?: FieldTranslationConfig
  /**
   * Config for document types with a single language field that determines the language for the whole document.
   */
  document?: DocumentTranslationConfig
  /**
   * A "style guide" that can be used to provide guidance on how to translate content.
   * Will be passed to the LLM - ergo this is only a guide and the model _may_ not
   * always follow it to the letter.
   *
   * When providing a function, consider caching the results of any async operation; it will invoked every time translate runs
   */
  styleguide?: TranslateStyleguide
}

export declare interface TranslationOutput {
  /** Language id */
  id: string
  outputPath: Path
}

export declare type TranslationOutputsFunction = (
  documentMember: DocumentMember,
  enclosingType: SchemaType,
  translateFromLanguageId: string,
  translateToLanguageIds: string[],
) => TranslationOutput[] | undefined

declare interface UserInputBlock {
  _type: typeof userInputTypeName
  _key: string
  message?: string
  description?: string
}

declare const userInputTypeName: 'sanity.assist.instruction.userInput'

/**
 * `useUserInput` returns a function that can be used to await user input.
 *
 * Useful for custom `fieldActions` to get user input for populating Agent Action requests,.
 *
 * ```ts
 *  fieldActions: {
 *   useFieldActions: (props) => {
 *     const {
 *       documentSchemaType,
 *       schemaId,
 *       getDocumentValue,
 *       getConditionalPaths,
 *       documentIdForAction,
 *     } = props
 *     const client = useClient({apiVersion: 'vX'})
 *     const getUserInput = useUserInput()
 *     return useMemo(() => {
 *       return [
 *         defineAssistFieldAction({
 *           title: 'Log user input',
 *           icon: UserIcon,
 *           onAction: async () => {
 *             const input = await getUserInput({
 *               title: 'Topic',
 *               inputs: [{id: 'about', title: 'What should the article be about?'}],
 *             })
 *             if (!input) return // user canceled input
 *             await client.agent.action.generate({
 *               schemaId,
 *               targetDocument: {
 *                 operation: 'createIfNotExists',
 *                 _id: documentIdForAction,
 *                 _type: documentSchemaType.name,
 *                 initialValues: getDocumentValue(),
 *               },
 *               instruction: `
 *                   Create a document about the following topic:
 *                   $about
 *                   ---
 *                `,
 *               instructionParams: {about: input[0].result},
 *               conditionalPaths: {paths: getConditionalPaths()},
 *             })
 *           },
 *         }),
 *       ]
 *     }, [
 *       client,
 *       documentSchemaType,
 *       schemaId,
 *       getDocumentValue,
 *       getConditionalPaths,
 *       documentIdForAction,
 *       getUserInput,
 *     ])
 *   },
 * }
 * ```
 */
export declare function useUserInput(): GetUserInput

export {}

declare module 'sanity' {
  interface ArrayOptions extends AssistOptions {}
  interface BlockOptions extends AssistOptions {}
  interface BooleanOptions extends AssistOptions {}
  interface CrossDatasetReferenceOptions extends AssistOptions {}
  interface DateOptions extends AssistOptions {}
  interface DatetimeOptions extends AssistOptions {}
  interface DocumentOptions extends AssistOptions {}
  interface FileOptions extends AssistOptions {}
  interface GeopointOptions extends AssistOptions {}
  interface ImageOptions {
    aiAssist?: AssistOptions['aiAssist'] & {
      /**
       * When set, an image will be created whenever the `imageInstructionField` is written to by
       * an AI Assist instruction.
       *
       * The value output by AI Assist will be use as an image prompt for an generative image AI.
       *
       * This means that instructions directly for the field or instructions that visit the field when running,
       * will result in the image being changed.
       *
       * `imageInstructionField` must be a child-path relative to the image field, ie:
       * * field
       * * path.to.field
       *
       * ### Example
       * ```ts
       * defineType({
       *   type: 'image',
       *   name: 'articleImage',
       *   fields: [
       *     defineField({
       *       type: 'text',
       *       name: 'imagePrompt',
       *       title: 'Image prompt',
       *       rows: 2,
       *     }),
       *   ],
       *   options: {
       *      aiAssist: {
       *        imageInstructionField: 'imagePrompt',
       *     }
       *   },
       * })
       * ```
       */
      imageInstructionField?: string
      /**
       * When set, an image description will be automatically created for the image.
       *
       * `imageDescriptionField` must be a child-path relative to the image field, ie:
       * * field
       * * path.to.field
       *
       * Whenever the image asset for the field is changed in the Studio,
       * an image description is generated and set into the `imageDescriptionField`.
       *
       * ### Example
       * ```ts
       * defineType({
       *   type: 'image',
       *   name: 'articleImage',
       *   fields: [
       *     defineField({
       *       type: 'string',
       *       name: 'altText',
       *       title: 'Alt text',
       *     }),
       *   ],
       *   options: {
       *      aiAssist: {
       *        imageDescriptionField: 'altText',
       *     }
       *   },
       * })
       * ```
       */
      imageDescriptionField?:
        | string
        | {
            path: string
            /**
             * When updateOnImageChange is true (or undefined), whenever the
             * image asset changes, imageDescriptionField will be regenerated.
             *
             * default:  true
             * */
            updateOnImageChange?: boolean
          }
    }
  }
  interface NumberOptions extends AssistOptions {}
  interface ObjectOptions extends AssistOptions {}
  interface ReferenceBaseOptions {
    aiAssist?: {
      /** Set to true to disable assistance for this field or type */
      exclude?: boolean
      /**
       * When set, the reference field will allow instructions to be added to it.
       * Should be the name of the embeddings-index where assist will look for contextually relevant documents
       * */
      embeddingsIndex?: string
    }
  }
  interface SlugOptions extends AssistOptions {}
  interface StringOptions extends AssistOptions {}
  interface TextOptions extends AssistOptions {}
  interface UrlOptions extends AssistOptions {}
  interface EmailOptions extends AssistOptions {}
}
