import {ElementType} from 'react'
import {InlineConfig} from 'vite'
import {NamedExoticComponent} from 'react'
import {ReactNode} from 'react'
import {RootTheme} from '@sanity/ui'
import {ThemeColorSchemeKey} from '@sanity/ui'

/** @internal */
export declare interface BooleanPropSchema extends GenericPropSchema<boolean> {
  type: 'boolean'
}

/** @internal */
export declare function createLocationStore(): WorkshopLocationStore

/** @internal */
export declare function createPubsub<Msg = unknown>(): Pubsub<Msg>

/** @internal */
export declare const DEFAULT_VIEWPORT_VALUE = 'auto'

/** @internal */
export declare const DEFAULT_ZOOM_VALUE = 1

/** @public */
export declare function defineConfig(config: WorkshopConfigOptions): WorkshopConfigOptions

/** @public */
export declare function defineRuntime(config: WorkshopRuntimeOptions): WorkshopRuntimeOptions

/** @public */
export declare function defineScope(scope: WorkshopScope): WorkshopScope

/** @internal */
export declare const EMPTY_ARRAY: never[]

/** @internal */
export declare const EMPTY_RECORD: Record<string, unknown>

/** @internal */
export declare interface GenericPropSchema<T = unknown> {
  name: string
  defaultValue?: T
  groupName?: string
}

/** @beta */
export declare function mount(options: {config: WorkshopConfig; element: HTMLElement | null}): void

/** @beta */
export declare function mountFrame(options: {
  config: WorkshopConfig
  element: HTMLElement | null
}): void

/** @internal */
export declare interface NumberPropSchema extends GenericPropSchema<number> {
  type: 'number'
}

/** @internal */
export declare type PropSchema =
  | BooleanPropSchema
  | NumberPropSchema
  | SelectPropSchema
  | StringPropSchema
  | TextPropSchema

/** @internal */
export declare interface PropsContextValue {
  registerProp: (propSchema: PropSchema) => void
  schemas: PropSchema[]
  setPropValue: (propName: string, value: unknown) => void
  unregisterProp: (propName: string) => void
  value: Record<string, unknown>
}

/** @internal */
export declare function propsPlugin(): WorkshopPlugin

/** @internal */
export declare interface PropsState {
  schemas: PropSchema[]
  value: Record<string, any>
}

/** @public */
export declare interface Pubsub<Msg = unknown> {
  publish: (msg: Msg) => void
  subscribe: (subscriber: (msg: Msg) => void) => () => void
}

/** @internal */
export declare type SelectPropOptionsProp<T extends SelectPropValue = SelectPropValue> =
  | Record<PropertyKey, T>
  | Record<Extract<T, PropertyKey>, T[keyof T]>
  | T[]
  | readonly T[]

/** @internal */
export declare interface SelectPropSchema<T extends SelectPropValue = SelectPropValue>
  extends GenericPropSchema<T> {
  type: 'select'
  options: SelectPropOptionsProp<T>
}

/** @internal */
export declare type SelectPropValue = string | number | boolean

/** @internal */
export declare interface StringPropSchema extends GenericPropSchema<string> {
  type: 'string'
}

/** @internal */
export declare interface TextPropSchema extends GenericPropSchema<string> {
  type: 'text'
}

/** @public */
export declare function useAction(
  name: string,
  options?: {
    preventDefault?: boolean
  },
): (...args: unknown[]) => void

/** @internal */
export declare function useBoolean(
  name: string,
  defaultValue?: boolean,
  groupName?: string,
): boolean | undefined

/** @internal */
export declare function useNumber(
  name: string,
  defaultValue?: number,
  groupName?: string,
): number | undefined

/** @internal */
export declare function useProps(): PropsContextValue

/** @internal */
export declare function useSelect<T extends SelectPropValue>(
  name: string,
  options: SelectPropOptionsProp<T>,
  defaultValue?: T,
  groupName?: string,
): T | undefined

/** @internal */
export declare function useString(
  name: string,
  defaultValue?: string,
  groupName?: string,
): string | undefined

/** @internal */
export declare function useText(
  name: string,
  defaultValue?: string,
  groupName?: string,
): string | undefined

/** @public */
export declare function useWorkshop<CustomMsg = never>(): WorkshopContextValue<CustomMsg>

/** @internal */
export declare const VIEWPORT_OPTIONS: {
  name: string
  title: string
  rect: {
    width: number | 'auto'
    height?: number
  }
}[]

/** @public */
export declare const Workshop: NamedExoticComponent<WorkshopProps>

/** @public */
export declare interface WorkshopCollection {
  name: string
  title: string
}

/** @public */
export declare interface WorkshopConfig {
  collections?: WorkshopCollection[]
  features?: {
    navbar?: boolean
  }
  frameUrl?: string
  plugins?: WorkshopPlugin<any>[]
  scopes: WorkshopScope[]
  theme?: RootTheme
  title?: string
}

/** @public */
export declare interface WorkshopConfigOptions extends Omit<WorkshopConfig, 'scopes'> {}

/** @public */
export declare interface WorkshopContextValue<CustomMsg = never> {
  plugins: WorkshopPlugin[]
  broadcast: (msg: WorkshopMsg | CustomMsg) => void
  channel: Pubsub<WorkshopMsg | CustomMsg>
  collections: WorkshopCollection[]
  frameReady: boolean
  frameUrl: string
  origin: 'frame' | 'main'
  path: string
  payload: Record<string, unknown>
  scheme: ThemeColorSchemeKey
  scope: WorkshopScope | null
  scopes: WorkshopScope[]
  story: WorkshopStory | null
  title: string
  viewport: string
  zoom: number
}

/** @internal */
export declare const WorkshopFrame: NamedExoticComponent<WorkshopFrameProps>

/** @internal */
export declare interface WorkshopFrameProps {
  config: WorkshopConfig
  setScheme: (nextScheme: ThemeColorSchemeKey) => void
}

/** @public */
export declare interface WorkshopFrameReadyMsg {
  type: 'workshop/frameReady'
}

/** @public */
export declare interface WorkshopLocation {
  type: 'pop' | 'push' | 'replace'
  path: string
  query?: WorkshopQuery
}

/** @public */
export declare interface WorkshopLocationStore {
  get: () => Omit<WorkshopLocation, 'type'>
  push: (nextLocation: Omit<WorkshopLocation, 'type'>) => void
  replace: (nextLocation: Omit<WorkshopLocation, 'type'>) => void
  subscribe: (subscriber: (nextLocation: WorkshopLocation) => void) => () => void
}

/** @public */
export declare type WorkshopMsg =
  | WorkshopFrameReadyMsg
  | WorkshopSetStateMsg
  | WorkshopSetZoomMsg
  | WorkshopSetViewportMsg
  | WorkshopToggleSchemeMsg
  | WorkshopSetSchemeMsg
  | WorkshopSetPathMsg
  | WorkshopSetPayloadMsg
  | WorkshopSetPayloadValueMsg

/** @public */
export declare interface WorkshopPlugin<Options = any> {
  name: string
  title: string
  inspector?: ElementType<{
    options: Options
  }>
  provider?: ElementType<{
    children?: ReactNode
    options: Options
  }>
  options?: Options
}

/** @public */
export declare interface WorkshopProps {
  config: WorkshopConfig
  locationStore: WorkshopLocationStore
  onSchemeChange: (nextScheme: ThemeColorSchemeKey) => void
  scheme?: ThemeColorSchemeKey
}

/** @internal */
export declare const WorkshopProvider: NamedExoticComponent<WorkshopProviderProps>

/** @internal */
export declare interface WorkshopProviderProps {
  broadcast: (msg: WorkshopMsg) => void
  children?: React.ReactNode
  channel: Pubsub<WorkshopMsg>
  config: WorkshopConfig
  frameReady: boolean
  origin: 'frame' | 'main'
  path: string
  payload: Record<string, unknown>
  scheme: ThemeColorSchemeKey
  viewport?: string
  zoom?: number
}

/** @public */
export declare type WorkshopQuery = {
  [key: string]: string | number | boolean | WorkshopQuery | undefined
}

/** @internal */
export declare function workshopReducer(state: WorkshopState, msg: WorkshopMsg): WorkshopState

/** @public */
export declare interface WorkshopRuntime {
  build?: {
    outDir?: string
  }
  pattern?: string | string[]
  server?: {
    port?: number
  }
  vite?: (viteConfig: InlineConfig) => InlineConfig
}

/** @public */
export declare interface WorkshopRuntimeOptions extends WorkshopRuntime {}

/** @public */
export declare interface WorkshopScope {
  name?: string
  title?: string
  stories: WorkshopStory[]
}

/** @public */
export declare interface WorkshopSetPathMsg {
  type: 'workshop/setPath'
  value: string
}

/** @public */
export declare interface WorkshopSetPayloadMsg {
  type: 'workshop/setPayload'
  value: Record<string, unknown>
}

/** @public */
export declare interface WorkshopSetPayloadValueMsg {
  type: 'workshop/setPayloadValue'
  key: string
  value: unknown
}

/** @public */
export declare interface WorkshopSetSchemeMsg {
  type: 'workshop/setScheme'
  value: ThemeColorSchemeKey
}

/** @public */
export declare interface WorkshopSetStateMsg {
  type: 'workshop/setState'
  value: WorkshopState
}

/** @public */
export declare interface WorkshopSetViewportMsg {
  type: 'workshop/setViewport'
  value: string
}

/** @public */
export declare interface WorkshopSetZoomMsg {
  type: 'workshop/setZoom'
  value: number
}

/** @public */
export declare interface WorkshopState {
  frameReady: boolean
  path: string
  payload: Record<string, unknown>
  scheme: ThemeColorSchemeKey
  viewport: string
  zoom: number
}

/** @public */
export declare interface WorkshopStory<Options = Record<string, unknown>> {
  name: string
  title: string
  component: ElementType
  options?: Options
}

/** @public */
export declare interface WorkshopToggleSchemeMsg {
  type: 'workshop/toggleScheme'
}

/** @internal */
export declare const ZOOM_OPTIONS: {
  value: number
  title: string
}[]

export {}
