import type { JSX, SVGProps } from 'react'
import type {
  OutputCondition,
  OutputFieldDefinition,
  PrimitiveValueType,
  SubBlockType,
} from '@sim/workflow-types/blocks'
import type { SelectorKey } from '@/hooks/selectors/types'
import type { ToolResponse } from '@/tools/types'

export type { OutputCondition, OutputFieldDefinition, PrimitiveValueType, SubBlockType }
export { isHiddenFromDisplay } from '@sim/workflow-types/blocks'

export type BlockIcon = (props: SVGProps<SVGSVGElement>) => JSX.Element
export type ParamType = 'string' | 'number' | 'boolean' | 'json' | 'array' | 'file'

export type BlockCategory = 'blocks' | 'tools' | 'triggers'

export enum IntegrationType {
  AI = 'ai',
  Analytics = 'analytics',
  Communication = 'communication',
  CRM = 'crm',
  CustomerSupport = 'customer-support',
  Databases = 'databases',
  Design = 'design',
  DeveloperTools = 'developer-tools',
  Documents = 'documents',
  Ecommerce = 'ecommerce',
  Email = 'email',
  FileStorage = 'file-storage',
  HR = 'hr',
  Other = 'other',
  Productivity = 'productivity',
  Sales = 'sales',
  Search = 'search',
  Security = 'security',
}

export type IntegrationTag =
  | 'marketing'
  | 'automation'
  | 'webhooks'
  | 'vector-search'
  | 'meeting'
  | 'calendar'
  | 'scheduling'
  | 'incident-management'
  | 'monitoring'
  | 'error-tracking'
  | 'prediction-markets'
  | 'document-processing'
  | 'ocr'
  | 'text-to-speech'
  | 'speech-to-text'
  | 'image-generation'
  | 'video-generation'
  | 'cloud'
  | 'google-workspace'
  | 'microsoft-365'
  | 'data-warehouse'
  | 'data-analytics'
  | 'customer-support'
  | 'project-management'
  | 'ticketing'
  | 'payments'
  | 'subscriptions'
  | 'enrichment'
  | 'web-scraping'
  | 'llm'
  | 'messaging'
  | 'version-control'
  | 'ci-cd'
  | 'note-taking'
  | 'spreadsheet'
  | 'seo'
  | 'email-marketing'
  | 'e-signatures'
  | 'identity'
  | 'secrets-management'
  | 'hiring'
  | 'sales-engagement'
  | 'agentic'
  | 'knowledge-base'
  | 'content-management'
  | 'forms'
  | 'link-management'
  | 'events'
  | 'feature-flags'

// Authentication modes for sub-blocks and summaries
export enum AuthMode {
  OAuth = 'oauth',
  ApiKey = 'api_key',
  BotToken = 'bot_token',
}

export type GenerationType =
  | 'javascript-function-body'
  | 'typescript-function-body'
  | 'json-schema'
  | 'json-object'
  | 'table-schema'
  | 'system-prompt'
  | 'custom-tool-schema'
  | 'sql-query'
  | 'postgrest'
  | 'mongodb-filter'
  | 'mongodb-pipeline'
  | 'mongodb-sort'
  | 'mongodb-documents'
  | 'mongodb-update'
  | 'neo4j-cypher'
  | 'neo4j-parameters'
  | 'timestamp'
  | 'timezone'
  | 'cron-expression'
  | 'odata-expression'

/**
 * Selector types that require display name hydration
 * These show IDs/keys that need to be resolved to human-readable names
 */
export const SELECTOR_TYPES_HYDRATION_REQUIRED: SubBlockType[] = [
  'oauth-input',
  'channel-selector',
  'user-selector',
  'file-selector',
  'sheet-selector',
  'folder-selector',
  'project-selector',
  'knowledge-base-selector',
  'workflow-selector',
  'document-selector',
  'variables-input',
  'mcp-server-selector',
  'mcp-tool-selector',
  'table-selector',
] as const

export type ExtractToolOutput<T> = T extends ToolResponse ? T['output'] : never

export type ToolOutputToValueType<T> = T extends Record<string, any>
  ? {
      [K in keyof T]: T[K] extends string
        ? 'string'
        : T[K] extends number
          ? 'number'
          : T[K] extends boolean
            ? 'boolean'
            : T[K] extends object
              ? 'json'
              : 'any'
    }
  : never

export type BlockOutput = PrimitiveValueType | { [key: string]: any }

interface ParamConfig {
  type: ParamType
  description?: string
  schema?: {
    type: string
    properties: Record<string, any>
    required?: string[]
    additionalProperties?: boolean
    items?: {
      type: string
      properties?: Record<string, any>
      required?: string[]
      additionalProperties?: boolean
    }
  }
}

export interface SubBlockConfig {
  id: string
  title?: string
  type: SubBlockType
  mode?: 'basic' | 'advanced' | 'both' | 'trigger' | 'trigger-advanced' // Default is 'both' if not specified. 'trigger' means only shown in trigger mode. 'trigger-advanced' is for advanced canonical pair members shown in trigger mode
  canonicalParamId?: string
  /** Controls parameter visibility in agent/tool-input context */
  paramVisibility?: 'user-or-llm' | 'user-only' | 'llm-only' | 'hidden'
  required?:
    | boolean
    | {
        field: string
        value: string | number | boolean | Array<string | number | boolean>
        not?: boolean
        and?: {
          field: string
          value: string | number | boolean | Array<string | number | boolean> | undefined
          not?: boolean
        }
      }
    | ((values?: Record<string, unknown>) => {
        field: string
        value: string | number | boolean | Array<string | number | boolean>
        not?: boolean
        and?: {
          field: string
          value: string | number | boolean | Array<string | number | boolean> | undefined
          not?: boolean
        }
      })
  defaultValue?: string | number | boolean | Record<string, unknown> | Array<unknown>
  options?:
    | {
        label: string
        id: string
        icon?: React.ComponentType<{ className?: string }>
        group?: string
        hidden?: boolean
        defaultChecked?: boolean
        description?: string
      }[]
    | (() => {
        label: string
        id: string
        icon?: React.ComponentType<{ className?: string }>
        group?: string
        hidden?: boolean
        defaultChecked?: boolean
        description?: string
      }[])
  min?: number
  max?: number
  columns?: string[]
  placeholder?: string
  password?: boolean
  readOnly?: boolean
  showCopyButton?: boolean
  connectionDroppable?: boolean
  hidden?: boolean
  hideFromPreview?: boolean // Hide this subblock from the workflow block preview
  showWhenEnvSet?: string // Show this subblock only when the named NEXT_PUBLIC_ env var is truthy
  hideWhenHosted?: boolean // Hide this subblock when running on hosted sim
  hideWhenEnvSet?: string // Hide this subblock when the named NEXT_PUBLIC_ env var is truthy
  description?: string
  tooltip?: string // Tooltip text displayed via info icon next to the title
  modalId?: string // Registry key when type is 'modal'; see sub-block/components/modal-registry.ts
  value?: (params: Record<string, any>) => string
  grouped?: boolean
  scrollable?: boolean
  maxHeight?: number
  selectAllOption?: boolean
  condition?:
    | {
        field: string
        value: string | number | boolean | Array<string | number | boolean>
        not?: boolean
        and?: {
          field: string
          value: string | number | boolean | Array<string | number | boolean> | undefined
          not?: boolean
        }
      }
    | ((values?: Record<string, unknown>) => {
        field: string
        value: string | number | boolean | Array<string | number | boolean>
        not?: boolean
        and?: {
          field: string
          value: string | number | boolean | Array<string | number | boolean> | undefined
          not?: boolean
        }
      })
  /**
   * Credential-type visibility gate. The first non-empty string value from
   * `watchFields` is treated as a credential ID and fetched via the credentials
   * API. The subblock is hidden unless `credential.type` matches `requiredType`.
   *
   * Only one subblock per block may use this. The serializer ignores it —
   * the field is always serialized when it has a value.
   */
  reactiveCondition?: {
    watchFields: string[]
    requiredType: 'oauth' | 'service_account'
  }
  // Props specific to 'code' sub-block type
  language?: 'javascript' | 'json' | 'python'
  generationType?: GenerationType
  collapsible?: boolean // Whether the code block can be collapsed
  defaultCollapsed?: boolean // Whether the code block is collapsed by default
  // OAuth specific properties - serviceId is the canonical identifier for OAuth services
  serviceId?: string
  requiredScopes?: string[]
  // Whether this credential selector supports credential sets (for trigger blocks)
  supportsCredentialSets?: boolean
  // Selector properties — declarative mapping to a SelectorKey
  selectorKey?: SelectorKey
  selectorAllowSearch?: boolean
  // File selector specific properties
  mimeType?: string
  // File upload specific properties
  acceptedTypes?: string
  multiple?: boolean
  maxSize?: number
  // Slider-specific properties
  step?: number
  integer?: boolean
  // Long input specific properties
  rows?: number
  // Multi-select functionality
  multiSelect?: boolean
  // Combobox specific: Enable search input in dropdown
  searchable?: boolean
  /** Dropdown-specific: include static options as Cmd K search entries that preset this subblock. */
  commandSearchable?: boolean
  // Wand configuration for AI assistance
  wandConfig?: {
    enabled: boolean
    prompt: string // Custom prompt template for this subblock
    generationType?: GenerationType // Optional custom generation type
    placeholder?: string // Custom placeholder for the prompt input
    maintainHistory?: boolean // Whether to maintain conversation history
  }
  /**
   * Declarative dependency hints for cross-field clearing or invalidation.
   * Supports two formats:
   * - Simple array: `['credential']` - all fields must have values (AND logic)
   * - Object with all/any: `{ all: ['authMethod'], any: ['credential', 'botToken'] }`
   *   - `all`: all listed fields must have values (AND logic)
   *   - `any`: at least one field must have a value (OR logic)
   */
  dependsOn?: string[] | { all?: string[]; any?: string[] }
  // Copyable-text specific: Use webhook URL from webhook management hook
  useWebhookUrl?: boolean
  // Dropdown/Combobox: Function to fetch options dynamically
  // Works with both 'dropdown' (select-only) and 'combobox' (editable with expression support)
  fetchOptions?: (blockId: string) => Promise<Array<{ label: string; id: string }>>
  // Dropdown/Combobox: Function to fetch a single option's label by ID (for hydration)
  // Called when component mounts with a stored value to display the correct label before options load
  fetchOptionById?: (
    blockId: string,
    optionId: string
  ) => Promise<{ label: string; id: string } | null>
}

export interface BlockConfig<T extends ToolResponse = ToolResponse> {
  type: string
  name: string
  description: string
  category: BlockCategory
  integrationType?: IntegrationType
  tags?: IntegrationTag[]
  longDescription?: string
  bestPractices?: string
  docsLink?: string
  bgColor: string
  icon: BlockIcon
  subBlocks: SubBlockConfig[]
  triggerAllowed?: boolean
  authMode?: AuthMode
  singleInstance?: boolean
  tools: {
    access: string[]
    config?: {
      tool: (params: Record<string, any>) => string
      params?: (params: Record<string, any>) => Record<string, any>
    }
  }
  inputs: Record<string, ParamConfig>
  outputs: Record<string, OutputFieldDefinition> & {
    visualization?: {
      type: 'image'
      url: string
    }
  }
  hideFromToolbar?: boolean
  triggers?: {
    enabled: boolean
    available: string[] // List of trigger IDs this block supports
  }
}

interface OutputConfig {
  type: BlockOutput
}
