<!--
  PubSubDemo.vue
  发布订阅模式演示 - 一条消息多消费者
-->
<template>
  <div class="pubsub-demo">
    <div class="header">
      <div class="title">发布订阅模式：一条消息，多处消费</div>
      <div class="subtitle">发布一次事件，多个订阅者独立处理</div>
    </div>

    <div class="main-flow">
      <div class="publisher-section">
        <div class="section-title">📤 发布者 Publisher</div>
        <div class="event-selector">
          <label>选择事件：</label>
          <select v-model="selectedEvent" @change="onEventChange">
            <option value="order.created">订单创建成功</option>
            <option value="user.registered">用户注册成功</option>
            <option value="product.updated">商品信息更新</option>
          </select>
        </div>
        <div class="event-details">
          <div class="event-name">{{ eventDetails.name }}</div>
          <div class="event-desc">{{ eventDetails.description }}</div>
        </div>
        <button
          class="publish-btn"
          @click="publishEvent"
          :disabled="publishing"
        >
          {{ publishing ? '发布中...' : '🚀 发布事件' }}
        </button>
      </div>

      <div class="topic-section">
        <div class="section-title">📡 主题 Topic</div>
        <div class="topic-box" :class="{ active: hasMessage }">
          <div class="topic-icon">📨</div>
          <div class="topic-name">{{ selectedEvent }}</div>
          <div v-if="hasMessage" class="message-indicator">消息已发布</div>
        </div>
        <div class="topic-desc">所有订阅者都会收到这条消息</div>
      </div>

      <div class="subscribers-section">
        <div class="section-title">📥 订阅者 Subscribers</div>
        <div class="subscribers-grid">
          <div
            v-for="sub in currentSubscribers"
            :key="sub.id"
            class="subscriber-card"
            :class="{ processing: sub.processing, completed: sub.completed }"
          >
            <div class="sub-icon">{{ sub.icon }}</div>
            <div class="sub-name">{{ sub.name }}</div>
            <div class="sub-action">{{ sub.action }}</div>
            <div class="sub-status">
              <span v-if="sub.processing">⏳ 处理中...</span>
              <span v-else-if="sub.completed">✅ 已完成</span>
              <span v-else>💤 等待消息</span>
            </div>
            <div class="sub-count">已处理: {{ sub.count }} 条</div>
          </div>
        </div>
      </div>
    </div>

    <div class="real-time-log">
      <div class="log-header">
        <div class="log-title">📋 实时日志</div>
        <button class="clear-btn" @click="clearLog">清空</button>
      </div>
      <div class="log-content">
        <div v-if="logs.length === 0" class="log-empty">暂无日志</div>
        <div
          v-for="(log, index) in logs"
          :key="index"
          class="log-entry"
          :class="log.type"
        >
          <span class="log-time">{{ log.time }}</span>
          <span class="log-message">{{ log.message }}</span>
        </div>
      </div>
    </div>

    <div class="use-cases">
      <div class="case-title">💡 典型应用场景</div>
      <div class="case-grid">
        <div class="case-card">
          <div class="case-icon">🛒</div>
          <div class="case-name">电商订单</div>
          <div class="case-desc">
            订单创建 → 库存服务、积分服务、通知服务、数据仓库同时处理
          </div>
        </div>
        <div class="case-card">
          <div class="case-icon">👤</div>
          <div class="case-name">用户注册</div>
          <div class="case-desc">
            用户注册 → 欢迎邮件、短信验证、发放优惠券、创建用户画像
          </div>
        </div>
        <div class="case-card">
          <div class="case-icon">📊</div>
          <div class="case-name">数据分析</div>
          <div class="case-desc">
            用户行为 → 推荐系统、实时统计、数据仓库、风控系统
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const selectedEvent = ref('order.created')
const publishing = ref(false)
const hasMessage = ref(false)
const logs = ref([])

const eventConfigs = {
  'order.created': {
    name: '订单创建成功',
    description: '用户完成支付，订单创建成功',
    subscribers: [
      {
        id: 1,
        name: '库存服务',
        icon: '📦',
        action: '扣减库存',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 2,
        name: '积分服务',
        icon: '💎',
        action: '增加积分',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 3,
        name: '短信服务',
        icon: '📱',
        action: '发送短信',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 4,
        name: '邮件服务',
        icon: '📧',
        action: '发送邮件',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 5,
        name: '数据仓库',
        icon: '📊',
        action: '记录订单数据',
        processing: false,
        completed: false,
        count: 0
      }
    ]
  },
  'user.registered': {
    name: '用户注册成功',
    description: '新用户完成注册流程',
    subscribers: [
      {
        id: 1,
        name: '欢迎邮件',
        icon: '📧',
        action: '发送欢迎邮件',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 2,
        name: '短信验证',
        icon: '📱',
        action: '发送验证短信',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 3,
        name: '优惠券服务',
        icon: '🎫',
        action: '发放新用户券',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 4,
        name: '用户画像',
        icon: '👤',
        action: '创建用户档案',
        processing: false,
        completed: false,
        count: 0
      }
    ]
  },
  'product.updated': {
    name: '商品信息更新',
    description: '商家更新商品信息',
    subscribers: [
      {
        id: 1,
        name: '搜索服务',
        icon: '🔍',
        action: '更新搜索索引',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 2,
        name: '推荐服务',
        icon: '⭐',
        action: '更新推荐列表',
        processing: false,
        completed: false,
        count: 0
      },
      {
        id: 3,
        name: '缓存服务',
        icon: '⚡',
        action: '刷新缓存',
        processing: false,
        completed: false,
        count: 0
      }
    ]
  }
}

const subscribers = ref(
  JSON.parse(JSON.stringify(eventConfigs['order.created'].subscribers))
)

const eventDetails = computed(() => {
  return eventConfigs[selectedEvent.value]
})

const currentSubscribers = computed(() => {
  return subscribers.value
})

const onEventChange = () => {
  subscribers.value = JSON.parse(
    JSON.stringify(eventConfigs[selectedEvent.value].subscribers)
  )
  hasMessage.value = false
}

const publishEvent = () => {
  if (publishing.value) return

  publishing.value = true
  hasMessage.value = true

  addLog('info', `📤 发布事件: ${eventDetails.value.name}`)

  // 所有订阅者都收到消息
  subscribers.value.forEach((sub, index) => {
    setTimeout(() => {
      sub.processing = true
      sub.completed = false
      addLog('info', `📥 ${sub.name} 开始处理`)

      // 模拟处理时间
      setTimeout(
        () => {
          sub.processing = false
          sub.completed = true
          sub.count++
          addLog('success', `✅ ${sub.name} 处理完成: ${sub.action}`)

          setTimeout(() => {
            sub.completed = false
          }, 2000)
        },
        1500 + Math.random() * 1000
      )
    }, index * 200)
  })

  setTimeout(() => {
    publishing.value = false
    setTimeout(() => {
      hasMessage.value = false
    }, 1000)
  }, 3000)
}

const addLog = (type, message) => {
  const now = new Date()
  const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
  logs.value.unshift({ type, time, message })
  logs.value = logs.value.slice(0, 20)
}

const clearLog = () => {
  logs.value = []
}
</script>

<style scoped>
.pubsub-demo {
  border: 1px solid var(--vp-c-divider);
  background: var(--vp-c-bg-soft);
  border-radius: 12px;
  padding: 1.5rem;
  margin: 1.5rem 0;
  font-family: var(--vp-font-family-base);
}

.header {
  margin-bottom: 1.5rem;
}

.title {
  font-weight: 700;
  font-size: 1.05rem;
}

.subtitle {
  color: var(--vp-c-text-2);
  font-size: 0.9rem;
  margin-top: 0.25rem;
}

.main-flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.5rem;
  margin-bottom: 1.5rem;
}

.section-title {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--vp-c-text-2);
  text-align: center;
  margin-bottom: 0.75rem;
  text-transform: uppercase;
}

.publisher-section,
.topic-section,
.subscribers-section {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.event-selector {
  width: 100%;
  margin-bottom: 1rem;
}

.event-selector label {
  display: block;
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
  color: var(--vp-c-text-2);
}

.event-selector select {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--vp-c-divider);
  border-radius: 6px;
  background: var(--vp-c-bg);
  font-size: 0.9rem;
}

.event-details {
  text-align: center;
  margin-bottom: 1rem;
  padding: 0.75rem;
  background: var(--vp-c-bg);
  border-radius: 6px;
  width: 100%;
}

.event-name {
  font-weight: 600;
  font-size: 0.95rem;
  margin-bottom: 0.35rem;
}

.event-desc {
  font-size: 0.8rem;
  color: var(--vp-c-text-2);
  line-height: 1.4;
}

.publish-btn {
  width: 100%;
  padding: 0.75rem;
  background: var(--vp-c-brand);
  color: white;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
  transition: all 0.2s;
}

.publish-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}

.publish-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.topic-box {
  background: var(--vp-c-bg);
  border: 2px solid var(--vp-c-divider);
  border-radius: 10px;
  padding: 0.75rem;
  text-align: center;
  min-width: 160px;
  transition: all 0.3s;
}

.topic-box.active {
  border-color: var(--vp-c-brand);
  background: rgba(59, 130, 246, 0.1);
  animation: pulse 2s infinite;
}

.topic-icon {
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

.topic-name {
  font-weight: 600;
  font-size: 0.85rem;
  margin-bottom: 0.5rem;
  font-family: monospace;
}

.message-indicator {
  margin-top: 0.5rem;
  padding: 0.35rem 0.5rem;
  background: #dcfce7;
  color: #166534;
  border-radius: 4px;
  font-size: 0.75rem;
  font-weight: 600;
}

.topic-desc {
  margin-top: 0.75rem;
  font-size: 0.8rem;
  color: var(--vp-c-text-3);
  text-align: center;
}

.subscribers-grid {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 100%;
}

.subscriber-card {
  background: var(--vp-c-bg);
  border: 2px solid var(--vp-c-divider);
  border-radius: 6px;
  padding: 0.75rem;
  transition: all 0.3s;
}

.subscriber-card.processing {
  border-color: #f59e0b;
  background: rgba(245, 158, 11, 0.05);
}

.subscriber-card.completed {
  border-color: #22c55e;
  background: rgba(34, 197, 94, 0.05);
}

.sub-icon {
  font-size: 1.5rem;
  margin-bottom: 0.25rem;
  text-align: center;
}

.sub-name {
  font-size: 0.85rem;
  font-weight: 600;
  text-align: center;
  margin-bottom: 0.25rem;
}

.sub-action {
  font-size: 0.75rem;
  color: var(--vp-c-text-2);
  text-align: center;
  margin-bottom: 0.35rem;
}

.sub-status {
  font-size: 0.75rem;
  text-align: center;
  margin-bottom: 0.25rem;
}

.sub-count {
  font-size: 0.7rem;
  color: var(--vp-c-text-3);
  text-align: center;
}

.real-time-log {
  background: var(--vp-c-bg);
  border-radius: 10px;
  padding: 0.75rem;
  border: 1px solid var(--vp-c-divider);
  margin-bottom: 1.5rem;
}

.log-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.75rem;
}

.log-title {
  font-weight: 600;
  font-size: 0.9rem;
}

.clear-btn {
  padding: 0.35rem 0.75rem;
  background: var(--vp-c-bg-soft);
  border: 1px solid var(--vp-c-divider);
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.8rem;
  transition: all 0.2s;
}

.clear-btn:hover {
  background: var(--vp-c-divider);
}

.log-content {
  max-height: 250px;
  
  background: var(--vp-c-bg-soft);
  border-radius: 6px;
  padding: 0.75rem;
  font-size: 0.8rem;
}

.log-empty {
  text-align: center;
  color: var(--vp-c-text-3);
  padding: 0.75rem;
}

.log-entry {
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--vp-c-divider);
  display: flex;
  gap: 0.5rem;
}

.log-entry:last-child {
  border-bottom: none;
}

.log-time {
  color: var(--vp-c-text-3);
  font-family: monospace;
}

.log-message {
  flex: 1;
}

.log-entry.info .log-message {
  color: var(--vp-c-text-1);
}

.log-entry.success .log-message {
  color: #16a34a;
}

.use-cases {
  background: var(--vp-c-bg);
  border-radius: 10px;
  padding: 0.75rem;
  border: 1px solid var(--vp-c-divider);
}

.case-title {
  font-weight: 600;
  margin-bottom: 1rem;
}

.case-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
}

.case-card {
  background: var(--vp-c-bg-soft);
  border-radius: 6px;
  padding: 0.75rem;
  text-align: center;
}

.case-icon {
  font-size: 2rem;
  margin-bottom: 0.5rem;
}

.case-name {
  font-weight: 600;
  font-size: 0.9rem;
  margin-bottom: 0.5rem;
}

.case-desc {
  font-size: 0.8rem;
  color: var(--vp-c-text-2);
  line-height: 1.5;
}

@keyframes pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
  }
  50% {
    box-shadow: 0 0 0 8px rgba(59, 130, 246, 0);
  }
}
</style>
