<script setup>
import { computed, onMounted, ref, watch, isVNode, onBeforeUnmount } from "vue";
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
defineOptions({
  name: "YhNotification"
});
const props = defineProps({
  title: { type: String, required: false },
  message: { type: [String, Object, Function], required: false },
  type: { type: null, required: false },
  icon: { type: [String, Object], required: false },
  showClose: { type: Boolean, required: false, default: true },
  duration: { type: Number, required: false, default: 4500 },
  offset: { type: Number, required: false, default: 16 },
  position: { type: null, required: false, default: "top-right" },
  id: { type: String, required: false },
  dangerouslyUseHTMLString: { type: Boolean, required: false, default: false },
  onClose: { type: Function, required: false },
  onClick: { type: Function, required: false },
  zIndex: { type: Number, required: false },
  customClass: { type: String, required: false },
  max: { type: Number, required: false },
  themeOverrides: { type: null, required: false }
});
const emit = defineEmits(["destroy", "click"]);
const ns = useNamespace("notification");
const { t } = useLocale();
const { themeStyle } = useComponentTheme(
  "notification",
  computed(() => props.themeOverrides)
);
const visible = ref(false);
let timer = null;
const notificationClasses = computed(() => [
  ns.b(),
  ns.m(props.position),
  props.type && ns.m(props.type),
  props.customClass
]);
const notificationStyles = computed(() => {
  const styles = {};
  if (props.position.startsWith("top")) {
    styles.top = `${props.offset}px`;
  } else {
    styles.bottom = `${props.offset}px`;
  }
  if (props.position.endsWith("center")) {
    styles.left = "50%";
    styles.transform = "translateX(-50%)";
  }
  if (props.zIndex) {
    styles.zIndex = String(props.zIndex);
  }
  return styles;
});
const positionClass = computed(() => {
  return props.position;
});
const iconName = computed(() => {
  if (props.icon) return props.icon;
  if (!props.type) return void 0;
  const iconMap = {
    success: "check-circle",
    warning: "warning",
    info: "info-circle",
    error: "close-circle"
  };
  return iconMap[props.type];
});
const renderMessage = computed(() => {
  const msg = props.message;
  if (typeof msg === "function") {
    return msg();
  }
  return msg;
});
const isMessageVNode = computed(() => {
  return isVNode(renderMessage.value);
});
const startTimer = () => {
  if (props.duration > 0) {
    timer = setTimeout(() => {
      close();
    }, props.duration);
  }
};
const clearTimer = () => {
  if (timer) {
    clearTimeout(timer);
    timer = null;
  }
};
const close = () => {
  clearTimer();
  visible.value = false;
  props.onClose?.();
};
onBeforeUnmount(() => {
  clearTimer();
});
const handleClick = () => {
  props.onClick?.();
  emit("click");
};
const handleMouseEnter = () => {
  clearTimer();
};
const handleMouseLeave = () => {
  startTimer();
};
const onDestroy = () => {
  emit("destroy");
};
watch(
  () => props.duration,
  () => {
    clearTimer();
    startTimer();
  }
);
onMounted(() => {
  visible.value = true;
  startTimer();
});
defineExpose({
  visible,
  close
});
</script>

<template>
  <transition :name="`${ns.b()}-${positionClass}`" @after-leave="onDestroy">
    <div
      v-show="visible"
      :class="notificationClasses"
      :style="[themeStyle, notificationStyles]"
      role="alert"
      @click="handleClick"
      @mouseenter="handleMouseEnter"
      @mouseleave="handleMouseLeave"
    >
      <!-- 图标 -->
      <div v-if="type || icon || $slots.icon" :class="ns.e('icon')">
        <slot name="icon">
          <span v-if="iconName" :class="ns.em('icon', type || 'default')">
            <svg
              v-if="type === 'success'"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                fill="currentColor"
                d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.808 536.384-99.52-99.584a38.4 38.4 0 1 0-54.336 54.336l126.72 126.72a38.272 38.272 0 0 0 54.336 0l262.4-262.464a38.4 38.4 0 1 0-54.272-54.336L456.192 600.384z"
              />
            </svg>
            <svg
              v-else-if="type === 'warning'"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                fill="currentColor"
                d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 192a58.432 58.432 0 0 0-58.24 63.744l23.36 256.384a35.072 35.072 0 0 0 69.76 0l23.296-256.384A58.432 58.432 0 0 0 512 256zm0 512a51.2 51.2 0 1 0 0-102.4 51.2 51.2 0 0 0 0 102.4z"
              />
            </svg>
            <svg
              v-else-if="type === 'info'"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                fill="currentColor"
                d="M512 64a448 448 0 1 1 0 896.064A448 448 0 0 1 512 64zm67.2 275.072c0-23.232-8.832-44.8-23.68-59.648-14.976-14.848-36.48-23.68-59.648-23.68-23.168 0-44.672 8.832-59.648 23.68-14.848 14.848-23.68 36.416-23.68 59.648 0 23.168 8.832 44.672 23.68 59.648 14.976 14.976 36.48 23.808 59.648 23.808 23.168 0 44.672-8.832 59.648-23.808 14.848-14.976 23.68-36.48 23.68-59.648zm-12.8 166.4h-128a32 32 0 0 0-32 32v256a32 32 0 0 0 32 32h128a32 32 0 0 0 32-32v-256a32 32 0 0 0-32-32z"
              />
            </svg>
            <svg
              v-else-if="type === 'error'"
              viewBox="0 0 1024 1024"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                fill="currentColor"
                d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336L512 457.664z"
              />
            </svg>
          </span>
        </slot>
      </div>

      <!-- 内容区域 -->
      <div :class="ns.e('content')">
        <!-- 标题 -->
        <div v-if="title" :class="ns.e('title')">{{ title }}</div>

        <!-- 消息内容 -->
        <div :class="ns.e('message')">
          <slot>
            <!-- VNode 类型（包括函数返回的 VNode） -->
            <component v-if="isMessageVNode" :is="renderMessage" :class="ns.e('text')" />
            <!-- HTML 字符串 -->
            <!-- eslint-disable vue/no-v-html -->
            <p
              v-else-if="dangerouslyUseHTMLString"
              :class="ns.e('text')"
              v-html="renderMessage"
            ></p>
            <!-- eslint-enable vue/no-v-html -->
            <!-- 普通字符串 -->
            <p v-else :class="ns.e('text')">
              {{ renderMessage }}
            </p>
          </slot>
        </div>
      </div>

      <!-- 关闭按钮 -->
      <div
        v-if="showClose"
        :class="ns.e('close')"
        :aria-label="t('message.close')"
        @click.stop="close"
      >
        <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
          <path
            fill="currentColor"
            d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"
          />
        </svg>
      </div>
    </div>
  </transition>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-notification {
  position: fixed;
  display: flex;
  width: 330px;
  padding: 20px;
  background-color: var(--yh-notification-bg-color);
  border: 1px solid var(--yh-notification-border-color);
  border-radius: var(--yh-border-radius-base);
  box-shadow: var(--yh-notification-shadow);
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
  overflow: hidden;
  box-sizing: border-box;
  z-index: var(--yh-z-index-modal);
}
.yh-notification--top-right {
  right: 16px;
}

.yh-notification--top-left {
  left: 16px;
}

.yh-notification--top-center {
  left: 50%;
  transform: translateX(-50%);
}

.yh-notification--bottom-right {
  right: 16px;
}

.yh-notification--bottom-left {
  left: 16px;
}

.yh-notification--bottom-center {
  left: 50%;
  transform: translateX(-50%);
}

.yh-notification--success {
  border-color: var(--yh-color-success-light-7);
}
.yh-notification--success .yh-notification__title {
  color: var(--yh-color-success);
}

.yh-notification--warning {
  border-color: var(--yh-color-warning-light-7);
}
.yh-notification--warning .yh-notification__title {
  color: var(--yh-color-warning);
}

.yh-notification--info {
  border-color: var(--yh-color-info-light-7);
}
.yh-notification--info .yh-notification__title {
  color: var(--yh-color-info);
}

.yh-notification--error {
  border-color: var(--yh-color-danger-light-7);
}
.yh-notification--error .yh-notification__title {
  color: var(--yh-color-danger);
}

.yh-notification__icon {
  margin-right: 12px;
  font-size: 24px;
  line-height: 24px;
  flex-shrink: 0;
}
.yh-notification__icon svg {
  width: 24px;
  height: 24px;
  vertical-align: top;
}
.yh-notification__icon--success {
  color: var(--yh-color-success);
}

.yh-notification__icon--warning {
  color: var(--yh-color-warning);
}

.yh-notification__icon--info {
  color: var(--yh-color-info);
}

.yh-notification__icon--error {
  color: var(--yh-color-danger);
}

.yh-notification__icon--default {
  color: var(--yh-text-color-regular);
}

.yh-notification__content {
  flex: 1;
  padding-right: 16px;
}

.yh-notification__title {
  margin: 0 0 8px;
  font-size: var(--yh-font-size-md);
  font-weight: var(--yh-font-weight-semibold);
  line-height: 24px;
  color: var(--yh-notification-title-color);
}

.yh-notification__message {
  margin: 0;
  font-size: var(--yh-font-size-base);
  line-height: 1.5;
}

.yh-notification__text {
  margin: 0;
  color: var(--yh-notification-content-color);
  word-break: break-word;
}

.yh-notification__close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 16px;
  height: 16px;
  cursor: pointer;
  color: var(--yh-text-color-secondary);
  transition: color var(--yh-duration-fast);
}
.yh-notification__close:hover {
  color: var(--yh-text-color-primary);
}
.yh-notification__close svg {
  width: 100%;
  height: 100%;
}

.yh-notification-top-right-enter-active, .yh-notification-top-right-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-top-right-enter-from, .yh-notification-top-right-leave-to {
  opacity: 0;
  transform: translateX(100%);
}

.yh-notification-top-left-enter-active, .yh-notification-top-left-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-top-left-enter-from, .yh-notification-top-left-leave-to {
  opacity: 0;
  transform: translateX(-100%);
}

.yh-notification-bottom-right-enter-active, .yh-notification-bottom-right-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-bottom-right-enter-from, .yh-notification-bottom-right-leave-to {
  opacity: 0;
  transform: translateX(100%);
}

.yh-notification-bottom-left-enter-active, .yh-notification-bottom-left-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-bottom-left-enter-from, .yh-notification-bottom-left-leave-to {
  opacity: 0;
  transform: translateX(-100%);
}

.yh-notification-top-center-enter-active, .yh-notification-top-center-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-top-center-enter-from, .yh-notification-top-center-leave-to {
  opacity: 0;
  transform: translateX(-50%) translateY(-100%);
}

.yh-notification-bottom-center-enter-active, .yh-notification-bottom-center-leave-active {
  transition: all var(--yh-duration-base) var(--yh-timing-ease-in-out);
}
.yh-notification-bottom-center-enter-from, .yh-notification-bottom-center-leave-to {
  opacity: 0;
  transform: translateX(-50%) translateY(100%);
}
</style>
