import type { Key } from '../_util/type';
import { Teleport, computed, defineComponent, onMounted, watch, onUnmounted } from 'vue';
import type { HTMLAttributes } from 'vue';
import type { MouseEventHandler } from '../_util/EventInterface';
import classNames from '../_util/classNames';

interface DivProps extends HTMLAttributes {
  // Ideally we would allow all data-* props but this would depend on https://github.com/microsoft/TypeScript/issues/28960
  'data-testid'?: string;
}

export interface NoticeProps {
  prefixCls: string;
  duration?: number | null;
  updateMark?: string;
  /** Mark as final key since set maxCount may keep the key but user pass key is different */
  noticeKey: Key;
  closeIcon?: any;
  closable?: boolean;
  props?: DivProps;
  onClick?: MouseEventHandler;
  onClose?: (key: Key) => void;

  /** @private Only for internal usage. We don't promise that we will refactor this */
  holder?: HTMLDivElement;

  /** @private Provided by CSSMotionList */
  visible?: boolean;
}

export default defineComponent<NoticeProps>({
  name: 'Notice',
  inheritAttrs: false,
  props: [
    'prefixCls',
    'duration',
    'updateMark',
    'noticeKey',
    'closeIcon',
    'closable',
    'props',
    'onClick',
    'onClose',
    'holder',
    'visible',
  ] as any,
  setup(props, { attrs, slots }) {
    let closeTimer: any;
    const duration = computed(() => (props.duration === undefined ? 1.5 : props.duration));
    const startCloseTimer = () => {
      if (duration.value) {
        closeTimer = setTimeout(() => {
          close();
        }, duration.value * 1000);
      }
    };

    const clearCloseTimer = () => {
      if (closeTimer) {
        clearTimeout(closeTimer);
        closeTimer = null;
      }
    };
    const close = (e?: MouseEvent) => {
      if (e) {
        e.stopPropagation();
      }
      clearCloseTimer();
      const { onClose, noticeKey } = props;
      if (onClose) {
        onClose(noticeKey);
      }
    };
    const restartCloseTimer = () => {
      clearCloseTimer();
      startCloseTimer();
    };
    onMounted(() => {
      startCloseTimer();
    });
    onUnmounted(() => {
      clearCloseTimer();
    });

    watch(
      [duration, () => props.updateMark, () => props.visible],
      ([preDuration, preUpdateMark, preVisible], [newDuration, newUpdateMark, newVisible]) => {
        if (
          preDuration !== newDuration ||
          preUpdateMark !== newUpdateMark ||
          (preVisible !== newVisible && newVisible)
        ) {
          restartCloseTimer();
        }
      },
      { flush: 'post' },
    );
    return () => {
      const { prefixCls, closable, closeIcon = slots.closeIcon?.(), onClick, holder } = props;
      const { class: className, style } = attrs;
      const componentClass = `${prefixCls}-notice`;
      const dataOrAriaAttributeProps = Object.keys(attrs).reduce(
        (acc: Record<string, string>, key: string) => {
          if (key.substr(0, 5) === 'data-' || key.substr(0, 5) === 'aria-' || key === 'role') {
            acc[key] = (attrs as any)[key];
          }
          return acc;
        },
        {},
      );
      const node = (
        <div
          class={classNames(componentClass, className, {
            [`${componentClass}-closable`]: closable,
          })}
          style={style}
          onMouseenter={clearCloseTimer}
          onMouseleave={startCloseTimer}
          onClick={onClick}
          {...dataOrAriaAttributeProps}
        >
          <div class={`${componentClass}-content`}>{slots.default?.()}</div>
          {closable ? (
            <a tabindex={0} onClick={close} class={`${componentClass}-close`}>
              {closeIcon || <span class={`${componentClass}-close-x`} />}
            </a>
          ) : null}
        </div>
      );

      if (holder) {
        return <Teleport to={holder} v-slots={{ default: () => node }}></Teleport>;
      }

      return node;
    };
  },
});