<template>
  <view 
    v-if="renderState"
    class="nana-toast-container"
    :style="{
      ...selectStyleType<ViewStyle, IToastPosition>(position, 'center', {
        center: {
          justifyContent: 'center',
          },
        top: {
          justifyContent: 'flex-start',
          paddingTop: theme.resolveThemeSize('ToastMarginWhenTop', 200),
        },
        bottom: {
          justifyContent: 'flex-end',
          paddingBottom: theme.resolveThemeSize('ToastMarginWhenBottom', 200),
        },
      }),
      pointerEvents: forbidClick ? 'auto' : 'none',
      ...maskStyle,
    }"
  >
    <view 
      :class="[
        'nana-toast',
        showState ? 'show' : 'hide',
      ]"
      :style="{
        pointerEvents: forbidClick ? 'auto' : 'none',
        ...themeStyles.toastStyle.value,
        ...toastStyle,
      }"
      @click="handleClick"
    >
      <ActivityIndicator 
        v-if="showProps.type === 'loading'"
        :color="textStyle?.color as string || themeStyles.toastTextStyle.value.color"
        :size="themeStyles.toastIconStyle.value.size"
      />
      <Icon
        v-else-if="showProps.type !== 'text' || showProps.icon"
        :icon="showProps.icon || iconType[showProps.type!]"
        :color="textStyle?.color as string || themeStyles.toastTextStyle.value.color"
        :size="themeStyles.toastIconStyle.value.size"
        v-bind="iconProps"
      />
      <Height v-if="showProps.type !== 'text'" :size="themeStyles.toastIconStyle.value.marginBottom" />
      <slot name="content">
        <text :style="{
          ...themeStyles.toastTextStyle.value,
          ...textStyle
        }">{{ showProps.content }}</text>
      </slot>
    </view>
  </view>
</template>

<script setup lang="ts">
import { nextTick, ref } from 'vue';
import ActivityIndicator from '../basic/ActivityIndicator.vue';
import type { IconProps } from '../basic/Icon.vue';
import { useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
import { DynamicColor, DynamicSize, DynamicSize2, DynamicVar, selectStyleType } from '../theme/ThemeTools';
import Icon from '../basic/Icon.vue';
import Height from '../layout/space/Height.vue';

export type IToastPosition = 'top'|'bottom'|'center';
export type IToastType = 'text'|'loading'|'success'|'fail'|'info'|'offline';

export interface ToastProps {
  /**
   * 图标自定义属性
   */
  iconProps?: IconProps;
  /**
   * 提示显示位置
   */
  position?: IToastPosition;
  /**
   * 是否禁止背景点击
   */
  forbidClick?: boolean;
  /**
   * 是否在点击后关闭
   */
  closeOnClick?: boolean;
  /**
   * 土司背景的自定义样式
   */
  maskStyle?: ViewStyle;
  /**
   * 土司提示容器的自定义样式
   */
  toastStyle?: ViewStyle;
  /**
   * 土司提示文字的自定义样式
   */
  textStyle?: TextStyle;
}
export interface ToastShowProps {
  /**
   * 自动关闭的延时，单位ms。
   * * 0：根据内容长度自动设置关闭时间，最小3000ms，最大15000ms
   * * -1：一直显示，直到手动关闭
   */
  duration?: number;
  /**
   * 图标类型
   */
  type?: IToastType;
  /**
   * 自定义图标
   */
  icon?: string;
  /**
   * 土司内容
   */
  content?: string;
  /**
   * 关闭后回调
   */
  onClose?: () => void;
}

const props = withDefaults(defineProps<ToastProps>(), {
  position: 'center',
  forbidClick: false,
  closeOnClick: true,
})

const theme = useTheme();
const iconType: {
  [key: string]: string
} = {
  success: theme.getVar('ToastIconSuccess', 'success'),
  fail: theme.getVar('ToastIconError', 'error'),
  offline: theme.getVar('ToastIconOffline', 'cry'),
  info: theme.getVar('ToastIconInfo', 'prompt'),
};

const themeStyles = theme.useThemeStyles({
  toastStyle: {
    backgroundColor: DynamicColor('ToastBackgroundColor', 'background.toast'),
    padding: DynamicSize2('ToastPaddingVertical', 'ToastPaddingHorizontal', 25, 30),
    borderRadius: DynamicSize('ToastRadius', 16),
  },
  toastTextStyle: {
    color: DynamicColor('ToastTextColor', 'white'),
  },
  toastIconStyle: {
    size: DynamicVar('ToastIconSize', 85),
    marginBottom: DynamicSize('ToastIconMarginBottom', 12),
  },
})

const renderState = ref(false);
const showState = ref(false);
const showProps = ref<ToastShowProps>({
  duration: 0,
  type: 'text',
  icon: '',
  content: '',
  onClose: () => {},
});
let showTimer = 0;

function show(options: ToastShowProps|string) {

  if (typeof options === 'string')
    options = { content: options };

  const { 
    duration = 0,
    type = 'text',
    icon = '',
    content = '',
    onClose = () => {},
  } = options;

  showState.value = false;
  renderState.value = true;
  showProps.value = {
    duration,
    type,
    icon,
    content,
    onClose,
  };
  if (showTimer) 
    clearTimeout(showTimer);
  setTimeout(() => showState.value = true, 10);

  let duration2 = duration;
  if (duration2 === 0)
    duration2 = Math.min(Math.max((content.length / 10) * 1000, 3000), 15000);

  if (duration2 > 0)
    showTimer = setTimeout(hide, duration2) as unknown as number;
}
function showWithType(type: IToastType, options: ToastShowProps|string) {
  show({ 
    type,
    content: typeof options === 'string' ? options : options.content,
    ...(typeof options === 'object' ? options : {}),
  })
}
function updateProps(options?: ToastShowProps) {
  showProps.value = {
    ...showProps.value,
    ...options,
  }
}
function hide() {
  if (showTimer)
    clearTimeout(showTimer);
  showProps.value.onClose?.();
  showState.value = false;
  showTimer = 0;
  showTimer = setTimeout(() => {
    renderState.value = false;
    showTimer = 0;
  }, 500) as unknown as number;
}
function handleClick() {
  if (props.closeOnClick)
    hide();
}

export interface ToastInstance {
  show(options: ToastShowProps|string) : void;
  info(options?: ToastShowProps|string): void;
  success(options?: ToastShowProps|string): void;
  fail(options?: ToastShowProps|string): void;
  offline(options?: ToastShowProps|string): void;
  loading(options?: ToastShowProps|string): void;
  text(options?: ToastShowProps|string): void;
  hide(): void;
  close(): void;
  updateProps(options?: ToastShowProps): void;
}

defineExpose<ToastInstance>({
  show,
  updateProps,
  info(options?: ToastShowProps|string) { showWithType('info', options as ToastShowProps); },
  success(options?: ToastShowProps|string) { showWithType('success', options as ToastShowProps); },
  fail(options?: ToastShowProps|string) { showWithType('fail', options as ToastShowProps); },
  offline(options?: ToastShowProps|string) { showWithType('offline', options as ToastShowProps); },
  loading(options?: ToastShowProps|string) { showWithType('loading', options as ToastShowProps); },
  text(options?: ToastShowProps|string) { showWithType('text', options as ToastShowProps); },
  hide,
  close: hide,
})

</script>

<style lang="scss">
.nana-toast-container {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 140;

  .nana-toast {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity ease-in-out 0.3s;

    &.show {
      opacity: 1;
    }
  }
}

</style>