<template>
  <div
    class="message-bubble"
    :class="[
      `role-${message.role}`,
      {
        'is-streaming': message.isStreaming,
        'is-end': !message.isEnd && message.role !== 'user',
        'is-error': message.isError,
        compact: compact,
      },
    ]"
    @mouseenter="isHovered = true"
    @mouseleave="isHovered = false"
  >
    <!-- 头像 -->
    <div class="avatar">
      <div class="avatar-inner" :class="message.role">
        <Bot v-if="message.role === 'assistant'" :size="20" />
        <User v-else :size="20" />
      </div>
    </div>

    <!-- 消息内容区域 -->
    <div class="message-content-wrapper">
      <!-- 角色名称 -->
      <div class="message-header">
        <span class="role-name">
          {{ message.role === "assistant" ? "AI 助手" : "你" }}
        </span>
        <span v-if="showTimestamp" class="timestamp">
          {{ formattedTime }}
        </span>
      </div>

      <!-- 消息主体 -->
      <div class="message-body">
        <!-- 错误状态 -->
        <div v-if="message.isError" class="error-content">
          <AlertCircle :size="18" />
          <span>{{ message.errorMessage || "消息发送失败" }}</span>
          <button class="retry-btn" @click="$emit('retry')">
            <RefreshCw :size="14" />
            重试
          </button>
        </div>

        <!-- 正常内容 -->
        <template v-else>
          <!-- 文本内容 - 使用 markstream-vue -->
          <div v-if="message.content.text" class="text-content markstream-vue">
            <MarkdownRender
              v-if="message.role !== 'user'"
              :content="message.content.text"
              :custom-html-tags="['think']"
              custom-id="playground-demo"
              :escape-html-tags="['question', 'answer']"
              @copy="textCopy"
            />
            <div v-else style="white-space: pre-wrap">
              {{ message.content.text }}
            </div>
          </div>

          <!-- 推荐选项 -->
          <div
            v-if="message.content.suggestions?.length && isNew"
            class="suggestions"
          >
            <button
              v-for="suggestion in message.content.suggestions"
              :key="suggestion.id"
              class="suggestion-btn"
              @click="$emit('select-suggestion', suggestion)"
            >
              <Zap :size="14" />
              {{ suggestion.text }}
            </button>
          </div>

          <!-- 图片展示 -->
          <div v-if="message.content.images?.length" class="images-grid">
            <div
              v-for="(image, index) in message.content.images"
              :key="image.id"
              class="image-item"
              @click="$emit('preview-image', image, index)"
            >
              <img :src="image.url" :alt="image.name" loading="lazy" />
              <div class="image-overlay">
                <Maximize2 :size="18" />
              </div>
            </div>
          </div>

          <!-- 单个视频 -->
          <div v-if="message.content.videos?.length === 1" class="single-video">
            <video
              :src="message.content.videos[0].url"
              :poster="message.content.videos[0].poster"
              controls
              preload="metadata"
            />
          </div>

          <!-- 多个视频 -->
          <div
            v-if="message.content.videos && message.content.videos.length > 1"
            class="videos-grid"
          >
            <div
              v-for="video in message.content.videos"
              :key="video.id"
              class="video-item"
              @click="$emit('play-video', video)"
            >
              <img :src="video.poster" :alt="video.title" />
              <div class="video-overlay">
                <Play :size="32" />
              </div>
              <span v-if="video.duration" class="video-duration">
                {{ formatDuration(video.duration) }}
              </span>
            </div>
          </div>

          <!-- 附件列表 -->
          <div v-if="message.content.files?.length" class="files-list">
            <div
              v-for="file in message.content.files"
              :key="file.id"
              class="file-item"
            >
              <div class="file-icon">
                {{ getFileEmoji(file.mimeType) }}
              </div>
              <div class="file-info">
                <span class="file-name">{{ file.name }}</span>
                <span class="file-size">{{ formatSize(file.size) }}</span>
              </div>
              <!-- <button
                class="download-btn"
                @click="$emit('download-file', file)"
              >
                <Download :size="16" />
              </button> -->
            </div>
          </div>
        </template>

        <!-- 加载动画 -->
        <div
          v-if="message.isStreaming && !message.content.text"
          class="loading-dots"
        >
          <span></span>
          <span></span>
          <span></span>
        </div>
      </div>

      <!-- 操作栏 -->
      <MessageActions
        v-if="
          message.role === 'assistant' &&
          !message.isStreaming &&
          !message.isError
        "
        :content="message.content.text || ''"
        :feedback="message.feedback"
        :show-regenerate="true"
        :is-hovered="isHovered"
        :is-new="isNew"
        :is-break="message.isBreak"
        @copy="handleCopy"
        @like="handleLike"
        @dislike="handleDislike"
        @regenerate="$emit('regenerate')"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { useClipboard } from "@vueuse/core";
import { ref, computed } from "vue";
// 正确导入 markstream-vue
import MarkdownRender from "markstream-vue";
import { setCustomComponents } from "markstream-vue";
import {
  Bot,
  User,
  AlertCircle,
  RefreshCw,
  Zap,
  Maximize2,
  Play,
  Download,
} from "@/components/icons";
import MessageActions from "./MessageActions.vue";
import { formatTimestamp, formatFileSize, getFileIcon } from "@/utils/helpers";
import type { Message, Suggestion, Attachment, VideoInfo } from "@/types/chat";
import ThinkingNode from "./components/ThinkingNode.vue";
import EChartsContainerNode from "./components/EChartsContainerNode.vue";

const props = withDefaults(
  defineProps<{
    message: Message;
    showTimestamp?: boolean;
    compact?: boolean;
    isNew?: boolean;
  }>(),
  {
    showTimestamp: true,
    compact: false,
  },
);
const { copy } = useClipboard({ legacy: true });
const emit = defineEmits<{
  retry: [];
  regenerate: [];
  copy: [];
  like: [];
  dislike: [];
  "select-suggestion": [suggestion: Suggestion];
  "preview-image": [image: Attachment, index: number];
  "play-video": [video: VideoInfo];
  "download-file": [file: Attachment];
}>();

const isHovered = ref(false);

const formattedTime = computed(() => {
  return formatTimestamp(props.message.timestamp);
});

function getFileEmoji(mimeType?: string) {
  return getFileIcon(mimeType || "");
}

function formatSize(size?: number) {
  return size ? formatFileSize(size) : "";
}

function formatDuration(seconds: number) {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, "0")}`;
}

function textCopy(data: any) {
  if (typeof data === "string") {
    copy(data);
  }
}

function handleCopy() {
  emit("copy");
}

function handleLike() {
  emit("like");
}

function handleDislike() {
  emit("dislike");
}

setCustomComponents("playground-demo", {
  think: ThinkingNode,
  vmr_container: EChartsContainerNode,
});
</script>

<style lang="scss" scoped>
.message-bubble {
  display: flex;
  gap: 16px;
  padding: 20px 150px;
  animation: fadeIn 0.3s ease;

  &.role-user {
    flex-direction: row-reverse;

    .message-content-wrapper {
      align-items: flex-end;
    }

    .message-header {
      flex-direction: row-reverse;
    }

    .message-body {
      background: #f8fafc;
      color: #1f2937;
      border-radius: 20px 20px 4px 20px;
    }

    .text-content {
      :deep(a) {
        color: #bfdbfe;
      }

      :deep(code) {
        background: rgba(255, 255, 255, 0.15);
        color: white;
      }
    }
  }

  &.role-assistant {
    .message-body {
      background: #f8fafc;
      border-radius: 20px 20px 20px 4px;

      .dark & {
        background: #2d2d3d;
      }
    }
  }

  &.compact {
    padding: 12px 20px;

    .avatar {
      width: 32px;
      height: 32px;

      svg {
        width: 16px;
        height: 16px;
      }
    }
  }

  // &.is-streaming {
  //   .message-body {
  //     position: relative;

  //     &::after {
  //       content: "";
  //       position: absolute;
  //       bottom: 12px;
  //       right: 12px;
  //       width: 8px;
  //       height: 8px;
  //       background: #3b82f6;
  //       border-radius: 50%;
  //       animation: pulse 1.5s infinite;
  //     }
  //   }
  // }

  &.is-end {
    .message-body {
      position: relative;

      &::after {
        content: "";
        position: absolute;
        bottom: 12px;
        right: 12px;
        width: 8px;
        height: 8px;
        background: #3b82f6;
        border-radius: 50%;
        animation: pulse 1.5s infinite;
      }
    }
  }
}

.avatar {
  flex-shrink: 0;
}

.avatar-inner {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 12px;

  &.assistant {
    background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%);
    color: white;
  }

  &.user {
    background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
    color: white;
  }
}

.message-content-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: 75%;
  min-width: 0;
}

.message-header {
  display: flex;
  align-items: center;
  gap: 10px;
}

.role-name {
  font-size: 14px;
  font-weight: 600;
  color: #1f2937;

  .dark & {
    color: #f3f4f6;
  }
}

.timestamp {
  font-size: 12px;
  color: #9ca3af;
}

.message-body {
  padding: 16px 20px;
  color: #1f2937;
  line-height: 1.7;

  .dark & {
    color: #e5e7eb;
  }
}

// markstream-vue 样式覆盖
.text-content {
  :deep(p) {
    margin: 0 0 12px;

    &:last-child {
      margin-bottom: 0;
    }
  }

  :deep(ul),
  :deep(ol) {
    margin: 12px 0;
    padding-left: 24px;
  }

  :deep(code:not(pre code)) {
    padding: 2px 6px;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.06);
    font-family: "JetBrains Mono", monospace;
    font-size: 0.9em;

    .dark & {
      background: rgba(255, 255, 255, 0.1);
    }
  }

  :deep(pre) {
    margin: 16px 0;
    border-radius: 12px;
    overflow: hidden;
  }

  :deep(a) {
    color: #3b82f6;
    text-decoration: none;

    &:hover {
      text-decoration: underline;
    }
  }

  :deep(table) {
    width: 100%;
    border-collapse: collapse;
    margin: 16px 0;

    th,
    td {
      border: 1px solid #e2e8f0;
      padding: 8px 12px;
      text-align: left;
    }

    th {
      background: #f8fafc;
      font-weight: 600;
    }

    .dark & {
      th,
      td {
        border-color: #374151;
      }

      th {
        background: #1e1e2e;
      }
    }
  }

  :deep(blockquote) {
    margin: 16px 0;
    padding: 12px 20px;
    border-left: 4px solid #3b82f6;
    background: rgba(59, 130, 246, 0.05);
    border-radius: 0 8px 8px 0;

    p {
      margin: 0;
    }
  }

  :deep(h1),
  :deep(h2),
  :deep(h3),
  :deep(h4) {
    margin: 20px 0 12px;
    font-weight: 600;
    line-height: 1.4;

    &:first-child {
      margin-top: 0;
    }
  }

  :deep(h1) {
    font-size: 1.5em;
  }
  :deep(h2) {
    font-size: 1.3em;
  }
  :deep(h3) {
    font-size: 1.15em;
  }
  :deep(h4) {
    font-size: 1em;
  }
}

.error-content {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  background: rgba(239, 68, 68, 0.1);
  border-radius: 10px;
  color: #ef4444;
  font-size: 14px;

  .retry-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    padding: 6px 12px;
    border: none;
    border-radius: 6px;
    background: #ef4444;
    color: white;
    font-size: 13px;
    cursor: pointer;
    transition: background 0.2s ease;

    &:hover {
      background: #dc2626;
    }
  }
}

.suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.suggestion-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border: 1px solid #e2e8f0;
  border-radius: 20px;
  background: white;
  color: #374151;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.2s ease;

  .dark & {
    background: #1e1e2e;
    border-color: #374151;
    color: #e5e7eb;
  }

  &:hover {
    border-color: #3b82f6;
    color: #3b82f6;
    background: rgba(59, 130, 246, 0.05);
  }

  svg {
    color: #f59e0b;
  }
}

.images-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 8px;
  margin-top: 12px;
}

.image-item {
  position: relative;
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.3s ease;
  }

  .image-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.4);
    color: white;
    opacity: 0;
    transition: opacity 0.2s ease;
  }

  &:hover {
    img {
      transform: scale(1.05);
    }

    .image-overlay {
      opacity: 1;
    }
  }
}

.single-video {
  margin-top: 12px;

  video {
    width: 100%;
    max-width: 480px;
    border-radius: 12px;
  }
}

.videos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
  margin-top: 12px;
}

.video-item {
  position: relative;
  aspect-ratio: 16/9;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;

  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .video-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.3);
    color: white;
    transition: background 0.2s ease;
  }

  .video-duration {
    position: absolute;
    bottom: 8px;
    right: 8px;
    padding: 2px 8px;
    background: rgba(0, 0, 0, 0.7);
    border-radius: 4px;
    color: white;
    font-size: 12px;
  }

  &:hover .video-overlay {
    background: rgba(0, 0, 0, 0.5);
  }
}

.files-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.file-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  background: rgba(0, 0, 0, 0.03);
  border-radius: 10px;

  .dark & {
    background: rgba(255, 255, 255, 0.05);
  }
}

.file-icon {
  font-size: 24px;
}

.file-info {
  flex: 1;
  min-width: 0;
}

.file-name {
  display: block;
  font-size: 14px;
  font-weight: 500;
  color: #1f2937;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

  .dark & {
    color: #f3f4f6;
  }
}

.file-size {
  font-size: 12px;
  color: #9ca3af;
}

.download-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 8px;
  background: rgba(59, 130, 246, 0.1);
  color: #3b82f6;
  cursor: pointer;
  transition: all 0.2s ease;

  &:hover {
    background: #3b82f6;
    color: white;
  }
}

.loading-dots {
  display: flex;
  gap: 6px;
  padding: 8px 0;

  span {
    width: 8px;
    height: 8px;
    background: #9ca3af;
    border-radius: 50%;
    animation: pulseDot 1.4s infinite ease-in-out both;

    &:nth-child(1) {
      animation-delay: -0.32s;
    }
    &:nth-child(2) {
      animation-delay: -0.16s;
    }
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(0.8);
  }
}

@keyframes pulseDot {
  0%,
  80%,
  100% {
    transform: scale(0.6);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}
</style>
