import { CustomIconName } from 'components/CustomIcon'
import { AllMachines } from 'hooks/useStateMachineCommands'
import {
  AnyStateMachine,
  ContextFrom,
  EventFrom,
  InterpreterFrom,
} from 'xstate'
import { Selection } from './selections'
import { Identifier, Value, VariableDeclaration } from 'lang/wasm'
import { commandBarMachine } from 'machines/commandBarMachine'

type Icon = CustomIconName
const PLATFORMS = ['both', 'web', 'desktop'] as const
const INPUT_TYPES = [
  'options',
  'string',
  'kcl',
  'selection',
  'boolean',
] as const
export interface KclExpression {
  valueAst: Value
  valueText: string
  valueCalculated: string
}
export interface KclExpressionWithVariable extends KclExpression {
  variableName: string
  variableDeclarationAst: VariableDeclaration
  variableIdentifierAst: Identifier
  insertIndex: number
}
export type KclCommandValue = KclExpression | KclExpressionWithVariable
export type CommandInputType = (typeof INPUT_TYPES)[number]

export type CommandSetSchema<T extends AnyStateMachine> = Partial<{
  [EventType in EventFrom<T>['type']]: Record<string, any>
}>

export type CommandSet<
  T extends AllMachines,
  Schema extends CommandSetSchema<T>
> = Partial<{
  [EventType in EventFrom<T>['type']]: Command<
    T,
    EventFrom<T>['type'],
    Schema[EventType]
  >
}>

export type CommandSetConfig<
  T extends AllMachines,
  Schema extends CommandSetSchema<T>
> = Partial<{
  [EventType in EventFrom<T>['type']]: CommandConfig<
    T,
    EventFrom<T>['type'],
    Schema[EventType]
  >
}>

export type Command<
  T extends AnyStateMachine = AnyStateMachine,
  CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
  CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
> = {
  name: CommandName
  ownerMachine: T['id']
  needsReview: boolean
  onSubmit: (data?: CommandSchema) => void
  onCancel?: () => void
  args?: {
    [ArgName in keyof CommandSchema]: CommandArgument<CommandSchema[ArgName], T>
  }
  displayName?: string
  description?: string
  icon?: Icon
  hide?: (typeof PLATFORMS)[number]
}

export type CommandConfig<
  T extends AnyStateMachine = AnyStateMachine,
  CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
  CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
> = Omit<
  Command<T, CommandName, CommandSchema>,
  'name' | 'ownerMachine' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
> & {
  needsReview?: true
  args?: {
    [ArgName in keyof CommandSchema]: CommandArgumentConfig<
      CommandSchema[ArgName],
      ContextFrom<T>
    >
  }
}

export type CommandArgumentConfig<
  OutputType,
  C = ContextFrom<AnyStateMachine>
> =
  | {
      description?: string
      required:
        | boolean
        | ((
            commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency
            machineContext?: C
          ) => boolean)
      skip?: boolean
    } & (
      | {
          inputType: 'options'
          options:
            | CommandArgumentOption<OutputType>[]
            | ((
                commandBarContext: {
                  argumentsToSubmit: Record<string, unknown>
                }, // Should be the commandbarMachine's context, but it creates a circular dependency
                machineContext?: C
              ) => CommandArgumentOption<OutputType>[])
          optionsFromContext?: (
            context: C
          ) => CommandArgumentOption<OutputType>[]
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: C
              ) => OutputType)
          defaultValueFromContext?: (context: C) => OutputType
        }
      | {
          inputType: 'selection'
          selectionTypes: Selection['type'][]
          multiple: boolean
        }
      | { inputType: 'kcl'; defaultValue?: string } // KCL expression inputs have simple strings as default values
      | {
          inputType: 'string'
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: C
              ) => OutputType)
          defaultValueFromContext?: (context: C) => OutputType
        }
      | {
          inputType: 'boolean'
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: C
              ) => OutputType)
          defaultValueFromContext?: (context: C) => OutputType
        }
    )

export type CommandArgument<
  OutputType,
  T extends AnyStateMachine = AnyStateMachine
> =
  | {
      description?: string
      required:
        | boolean
        | ((
            commandBarContext: { argumentsToSubmit: Record<string, unknown> }, // Should be the commandbarMachine's context, but it creates a circular dependency
            machineContext?: ContextFrom<T>
          ) => boolean)
      skip?: boolean
      machineActor: InterpreterFrom<T>
    } & (
      | {
          inputType: Extract<CommandInputType, 'options'>
          options:
            | CommandArgumentOption<OutputType>[]
            | ((
                commandBarContext: {
                  argumentsToSubmit: Record<string, unknown>
                }, // Should be the commandbarMachine's context, but it creates a circular dependency
                machineContext?: ContextFrom<T>
              ) => CommandArgumentOption<OutputType>[])
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: ContextFrom<T>
              ) => OutputType)
        }
      | {
          inputType: 'selection'
          selectionTypes: Selection['type'][]
          multiple: boolean
        }
      | { inputType: 'kcl'; defaultValue?: string } // KCL expression inputs have simple strings as default value
      | {
          inputType: 'string'
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: ContextFrom<T>
              ) => OutputType)
        }
      | {
          inputType: 'boolean'
          defaultValue?:
            | OutputType
            | ((
                commandBarContext: ContextFrom<typeof commandBarMachine>,
                machineContext?: ContextFrom<T>
              ) => OutputType)
        }
    )

export type CommandArgumentWithName<
  OutputType,
  T extends AnyStateMachine = AnyStateMachine
> = CommandArgument<OutputType, T> & {
  name: string
}

export type CommandArgumentOption<A> = {
  name: string
  isCurrent?: boolean
  value: A
}
