import { message } from 'ant-design-vue/es'
import type { MessageArgsProps } from 'ant-design-vue/es'
import type { VueNode } from 'ant-design-vue/es/_util/type'
import type { VNode } from 'vue'
import { isPrimitiveValue } from 'nocodb-sdk'
import NcAlert, { type NcAlertProps } from '../components/nc/Alert.vue'
import { getI18n } from '~/plugins/a.i18n'

interface NcAlertMessageProps
  extends Pick<
    NcAlertProps,
    | 'showIcon'
    | 'closable'
    | 'align'
    | 'copyText'
    | 'copyBtnTooltip'
    | 'messageClass'
    | 'descriptionClass'
    | 'duration'
    | 'showDuration'
  > {}

/**
 * `NcMessageObjectProps` defines the properties allowed in `ncMessage`,
 * extending `NcAlertProps` while omitting fields that are irrelevant for messages.
 */
export interface NcMessageObjectProps extends NcAlertMessageProps, Omit<MessageArgsProps, 'type' | 'content'> {
  title?: string
  content?: string | (() => VueNode) | VueNode
  /**
   * Custom action slot content to be rendered inside the alert.
   * It can be either a `VueNode` or a function returning a `VueNode`.
   */
  action?: VNode | (() => VNode)
  showDefaultMessage?: boolean
  showCopyBtn?: boolean
  /**
   * For internal use only
   */
  renderAsNcAlert?: boolean
}

/**
 * `NcMessageProps` can either be a string (message text) or an object of type `NcMessageObjectProps`.
 */
export type NcMessageProps = NcMessageObjectProps | VueNode

/**
 * Use `copyText` & `copyBtnTooltip` to set the copy text & tooltip for the copy button if params is primitive value
 */
export interface NcMessageExtraProps
  extends Pick<NcMessageObjectProps, 'showDefaultMessage' | 'showCopyBtn' | 'copyText' | 'copyBtnTooltip'> {}

const defaultNcMessageExtraProps = {
  showDefaultMessage: false,
  showCopyBtn: true,
}

/**
 * Default values for `NcMessageObjectProps`.
 */
const initialValue = {
  title: '',
  content: '',
  showIcon: true,
  closable: true,
  align: 'top',
  class: '',
  messageClass: '',
  descriptionClass: '',
  renderAsNcAlert: true,
  ...defaultNcMessageExtraProps,
} as NcMessageObjectProps

const initialToastTypeValue = {
  closable: false,
  showCopyBtn: false,
  showDuration: false,
  showIcon: false,
  duration: 2,
} as NcMessageObjectProps

/**
 * Generates a unique key for each message instance.
 * @returns A unique string key for the message.
 */
const generateMessageKey = (params: NcMessageProps) => {
  if (ncIsString(params) || !params.key) {
    return `ncMessage_${Date.now()}_${Math.random()}`
  }

  return params.key
}

function isNcMessageObjectProps(params: any): params is NcMessageObjectProps {
  return !ncIsEmptyObject(params)
}

/**
 * Processes `NcMessageProps` and merges them with default values.
 * - If a string is provided, it sets it as the description while applying a default content based on type.
 * - If neither `content` nor `description` exist, the content is set to the default localized text.
 * - Uses the spread operator to ensure proper merging of values.
 *
 * @param type - The type of message (`success`, `error`, `info`, `warning`).
 * @param params - The message parameters, either a string or an object.
 * @param ncMessageExtraProps - The extra props
 * @returns A full `NcMessageObjectProps` object with defaults applied.
 */
const getMessageProps = (
  type: NcAlertProps['type'],
  params: NcMessageProps,
  ncMessageExtraProps: NcMessageExtraProps = defaultNcMessageExtraProps,
): NcMessageObjectProps => {
  const updatedParams = { ...initialValue, ...(type === 'toast' ? initialToastTypeValue : {}) }
  let content = ''

  if (type === 'toast') {
    if (isPrimitiveValue(params)) {
      return {
        ...updatedParams,
        content: params,
        renderAsNcAlert: true,
      }
    } else {
      return {
        ...updatedParams,
        ...params,
        renderAsNcAlert: true,
      }
    }
  } else if (isPrimitiveValue(params)) {
    content = params?.toString() ?? ''
    // If params is a string, use it as the description and apply a default message based on type
    return {
      ...updatedParams,
      title: ncMessageExtraProps.showDefaultMessage || !content ? getI18n().global.t(`objects.ncMessage.${type}`) : '',
      content,
      copyText: ncMessageExtraProps.showCopyBtn ? ncMessageExtraProps.copyText ?? content ?? '' : '',
    }
  } else if (!isNcMessageObjectProps(params)) {
    content = params
    return {
      ...updatedParams,
      title: ncMessageExtraProps.showDefaultMessage || !content ? getI18n().global.t(`objects.ncMessage.${type}`) : '',
      content,
      renderAsNcAlert: false,
    }
  } else {
    const showDefaultMessage = ncMessageExtraProps.showDefaultMessage ?? params.showDefaultMessage

    /**
     * default value of `ncMessageExtraProps.showCopyBtn` & `params.showCopyBtn` is true, so if one is false then we should not show
     */
    const showCopyBtn = ncMessageExtraProps.showCopyBtn && params.showCopyBtn

    let copyText = ''

    if (showCopyBtn) {
      if (params?.copyText || ncMessageExtraProps.copyText) {
        copyText = params?.copyText || ncMessageExtraProps.copyText
      } else if (isPrimitiveValue(params.content)) {
        copyText = params.content?.toString() ?? params.title ?? ''
      }
    }

    // If neither title nor content exist, set message to the default localized text
    const showDefaultTitle = !updatedParams.title && !updatedParams.content && showDefaultMessage

    // Merge provided object properties into the default values using the spread operator
    return {
      ...updatedParams,
      ...params,
      copyText,
      ...(showDefaultTitle ? { title: getI18n().global.t(`objects.ncMessage.${type}`) } : {}),
      renderAsNcAlert: isPrimitiveValue(params.content),
    }
  }
}

/**
 * Displays a message using Ant Design's `message.open`, rendering an `NcAlert` inside.
 * Note: we have to render our `NcAlert` only if content is primitive value
 * @param type - The type of message (`success`, `error`, `info`, `warning`).
 * @param params - The message content or properties.
 * @param duration - Optional duration in seconds before auto-dismissal.
 *
 */

const showMessage = (
  type: NcAlertProps['type'],
  params: NcMessageProps,
  duration?: number,
  ncMessageExtraProps?: NcMessageExtraProps,
) => {
  const props = getMessageProps(type, params, ncMessageExtraProps)

  const {
    onClick,
    onClose,
    content,
    title,
    getPopupContainer,
    style,
    appContext,
    prefixCls = '',
    rootPrefixCls = '',
    renderAsNcAlert,
    showCopyBtn: _showCopyBtn,
    showDefaultMessage: _showDefaultMessage,
    ...ncAlertProps
  } = props

  // If title & content is blank then no need to show blank toast message
  if (!title && !content) return

  const key = generateMessageKey(params)

  let ncDuration = duration ?? ncAlertProps.duration

  // If showDuration is false and duration is not provided, then set duration to 3 seconds
  if (!ncAlertProps.showDuration && !ncDuration) {
    ncDuration = 3
  }

  return message.open({
    key,
    content: renderAsNcAlert
      ? () =>
          h(
            NcAlert,
            {
              ...ncAlertProps,
              message: title,
              description: content,
              type,
              isNotification: true,
              onClose: () => {
                onClose?.()
                message.destroy(key)
              },
              duration: ncDuration,
            },
            {
              action: ncIsFunction(ncAlertProps.action)
                ? ncAlertProps.action
                : ncAlertProps.action
                ? () => ncAlertProps.action
                : undefined,
              icon: ncIsFunction(ncAlertProps.icon) ? ncAlertProps.icon : ncAlertProps.icon ? () => ncAlertProps.icon : undefined,
            },
          )
      : content,
    type: !renderAsNcAlert && type !== 'toast' ? type : undefined,
    duration: ncDuration,
    prefixCls,
    rootPrefixCls,
    getPopupContainer,
    onClose() {
      onClose?.()
    },
    style,
    appContext,
    onClick(event) {
      onClick?.(event)
    },
  })
}

/**
 * `ncMessage` provides methods to display different types of messages.
 *
 * ## Usage Examples:
 *
 * ### Display a default success message (title based on type)
 * ```ts
 * ncMessage.success();
 * ncMessage.error();
 * ncMessage.info();
 * ncMessage.warn();
 * ```
 *
 * ### Display a success message with a custom title only
 * ```ts
 * ncMessage.success({
 *   title: 'Table created successfully',
 * });
 * ```
 *
 * ### Display a success message with a custom description only
 * ```ts
 * ncMessage.success({
 *   content: 'Lorem ipsum dolor sit amet, consectetur adipiscing.',
 * });
 * ```
 *
 * ### Display a default title based on type with a custom description
 * ```ts
 * ncMessage.success('Lorem ipsum dolor sit amet, consectetur adipiscing', undefined, { showDefaultMessage: false });
 * ncMessage.error('Lorem ipsum dolor sit amet, consectetur adipiscing', undefined, { showDefaultMessage: false });
 * ncMessage.info('Lorem ipsum dolor sit amet, consectetur adipiscing', undefined, { showDefaultMessage: false });
 * ncMessage.warn('Lorem ipsum dolor sit amet, consectetur adipiscing', undefined, { showDefaultMessage: false });
 * ```
 *
 * ### Fully customized success message
 * ```ts
 * ncMessage.success({
 *   title: 'Table created successfully',
 *   content: 'Lorem ipsum dolor sit amet, consectetur adipiscing',
 *   copyText: 'Lorem ipsum dolor sit amet, consectetur adipiscing',
 *   showIcon: true,
 *   closable: true,
 *   showDuration: false,
 *   align: 'center',
 *   action: h(
 *     resolveComponent('NcButton'),
 *     {
 *       onClick: () => {
 *         console.log('clicked')
 *       },
 *       size: 'small',
 *       type: 'secondary',
 *     },
 *     () => 'Okay',
 *   ),
 *   icon: () => h(resolveComponent('GeneralIcon'), { icon: 'settings', class: 'text-2xl text-red-500' }),
 * });
 * ```
 */

const ncMessage = {
  success: (params: NcMessageProps = '', duration?: number, ncMessageExtraProps?: NcMessageExtraProps) => {
    return showMessage('success', params, duration, ncMessageExtraProps)
  },

  error: (params: NcMessageProps = '', duration?: number, ncMessageExtraProps?: NcMessageExtraProps) => {
    return showMessage('error', params, duration, ncMessageExtraProps)
  },

  info: (params: NcMessageProps = '', duration?: number, ncMessageExtraProps?: NcMessageExtraProps) => {
    return showMessage('info', params, duration, ncMessageExtraProps)
  },

  warn: (params: NcMessageProps = '', duration?: number, ncMessageExtraProps?: NcMessageExtraProps) => {
    return showMessage('warning', params, duration, ncMessageExtraProps)
  },

  warning: (params: NcMessageProps = '', duration?: number, ncMessageExtraProps?: NcMessageExtraProps) => {
    return showMessage('warning', params, duration, ncMessageExtraProps)
  },

  toast: (
    params:
      | (Omit<NcMessageObjectProps, 'content'> & {
          content: string | number | null | undefined
        })
      | string
      | number
      | null
      | undefined = '',
    duration?: number,
    ncMessageExtraProps?: NcMessageExtraProps,
  ) => {
    return showMessage('toast', params, duration, ncMessageExtraProps)
  },
}

export { ncMessage }
