<!-- =============================================================================
// 企微IT智能服务台 — 坐席端输入框组件
// =============================================================================
// 说明：坐席端回复消息的输入区域
// 功能：
//    - 输入框默认3行高度，自动扩展（max-height: 150px）
//    - 底部显示字数统计（当前/最大，如：120/500）
//    - 右下角发送按钮（icon）
//    - Enter键发送，Shift+Enter换行
//    - 空内容时禁用发送按钮
//    - 支持粘贴图片/文件上传
//    - 快捷工具栏（表情/图片/截图/文件/语音/快速回复）
// ============================================================================= -->

<template>
  <div class="input-box" ref="replyBoxRef">
    <!-- 引用回复预览（回复某条消息时显示） -->
    <div v-if="replyToMessage" class="reply-preview">
      <div class="reply-preview-bar"></div>
      <div class="reply-preview-content">
        <span class="reply-preview-sender">{{ replyToMessage.sender_name || '未知' }}</span>
        <span class="reply-preview-text">{{ replyToMessage.content?.substring(0, 60) }}{{ replyToMessage.content?.length > 60 ? '...' : '' }}</span>
      </div>
      <button class="reply-preview-close" title="取消回复" @click="$emit('cancelReply')">✕</button>
    </div>

    <!-- 快捷工具栏 -->
    <div class="chat-toolbar">
      <div class="emoji-wrapper">
        <button class="tb-btn" title="表情" @click="showEmojiPicker = !showEmojiPicker">
          😊
          <span class="tb-tip">表情</span>
        </button>
        <!-- 自定义中文表情选择面板 -->
        <div v-if="showEmojiPicker" class="emoji-picker-popup">
          <div class="emoji-grid">
            <button
              v-for="emoji in commonEmojis"
              :key="emoji"
              class="emoji-grid__item"
              @click="onEmojiSelect(emoji)"
            >{{ emoji }}</button>
          </div>
        </div>
        <div v-if="showEmojiPicker" class="emoji-picker-overlay" @click="showEmojiPicker = false"></div>
      </div>
      <button class="tb-btn" title="图片" @click="handleToolbarClick('image')">
        🖼
        <span class="tb-tip">图片</span>
      </button>
      <button class="tb-btn" title="截图" @click="handleToolbarClick('screenshot')">
        ✂
        <span class="tb-tip">截图</span>
      </button>
      <button class="tb-btn" title="文件" @click="handleToolbarClick('file')">
        📎
        <span class="tb-tip">文件</span>
      </button>
      <button class="tb-btn" title="语音" @click="handleToolbarClick('voice')">
        🎤
        <span class="tb-tip">语音</span>
      </button>
      <div class="tb-sep"></div>
      <button class="tb-btn" title="快速回复" @click="handleToolbarClick('quickReply')">
        ⚡
        <span class="tb-tip">快速回复</span>
      </button>
    </div>

    <!-- 输入行 -->
    <div class="chat-input-card">
      <textarea
        ref="inputRef"
        v-model="inputText"
        class="chat-input"
        placeholder="输入回复内容... (Enter发送，Shift+Enter换行)"
        :style="{ height: textareaHeight + 'px' }"
        :disabled="!conversationStore.currentConversation"
        @keydown="handleKeydown"
        @input="handleInput"
        @paste="handlePaste"
      ></textarea>

      <!-- 发送按钮 — icon样式 -->
      <button
        class="btn-send"
        :class="{ 'btn-send--active': canSend }"
        :disabled="!canSend"
        @click="handleSend"
      >
        <svg v-if="!conversationStore.loading" class="send-icon" viewBox="0 0 24 24" fill="currentColor">
          <path d="M2.01 21L23 12 2.01 3 2 10l15-2-15-2z"/>
        </svg>
        <van-loading v-else size="16px" color="#fff" />
      </button>
    </div>

    <!-- 字数统计 -->
    <div class="input-box__counter">
      {{ charCount }}/{{ maxChars }}
    </div>

    <!-- 邀请弹窗 -->
    <InviteParticipantDialog
      v-model="showInviteDialog"
      :conversation-id="conversationStore.currentConversation?.id || ''"
      :existing-participant-ids="existingParticipantIds"
      @success="onInviteSuccess"
    />

    <!-- 隐藏的文件输入框 -->
    <input
      ref="fileInputRef"
      type="file"
      style="display: none"
      @change="handleFileSelect"
    />

    <!-- 截图编辑器 -->
    <ScreenshotEditor
      v-if="showScreenshotEditor"
      :screenshot-canvas="screenshotCanvas"
      @confirm="onScreenshotConfirm"
      @cancel="onScreenshotCancel"
    />
  </div>
</template>

<script setup lang="ts">
/**
 * InputBox 坐席端输入框组件
 * 输入框默认3行高度，自动扩展
 * 底部显示字数统计，右下角发送按钮
 * Enter发送，Shift+Enter换行
 */
import { ref, computed, watch, nextTick, onUnmounted } from 'vue'
import { ElMessage } from 'element-plus'
import html2canvas from 'html2canvas-pro'
import { useConversationStore } from '@/stores/conversation'
import InviteParticipantDialog from '@/components/conversation/InviteParticipantDialog.vue'
import ScreenshotEditor from './ScreenshotEditor.vue'
import { uploadFile } from '@/api/upload'
import { sendMessage } from '@/api/message'
import type { Message } from '@/api/message'

// ============================================================================
// 工具函数
// ============================================================================
function formatErrorDetail(detail: any): string {
  if (!detail) return ''
  if (typeof detail === 'string') return detail
  if (Array.isArray(detail)) {
    return detail.map((d: any) => {
      if (d.loc && d.msg) return `${d.loc.slice(1).join('.')}: ${d.msg}`
      if (d.msg) return d.msg
      return JSON.stringify(d)
    }).join('; ')
  }
  if (typeof detail === 'object') return JSON.stringify(detail)
  return String(detail)
}

// ============================================================================
// Props & Emits
// ============================================================================
interface Props {
  /** 引用回复：正在回复的消息 */
  replyToMessage?: Message | null
}

interface Emits {
  (e: 'send', content: string): void
  (e: 'cancelReply'): void
}

withDefaults(defineProps<Props>(), {
  replyToMessage: null,
})

const emit = defineEmits<Emits>()

// ============================================================================
// 状态
// ============================================================================
const conversationStore = useConversationStore()

/** 输入框文本 */
const inputText = ref('')

/** 输入框 DOM 引用 */
const inputRef = ref<HTMLTextAreaElement | null>(null)

/** 隐藏文件输入框 DOM 引用 */
const fileInputRef = ref<HTMLInputElement | null>(null)

/** ReplyBox 容器 DOM 引用 */
const replyBoxRef = ref<HTMLElement | null>(null)

/** textarea 高度 */
const textareaHeight = ref(60)

/** 最大字符数 */
const maxChars = 500

/** 是否显示表情面板 */
const showEmojiPicker = ref(false)

/** 邀请弹窗是否可见 */
const showInviteDialog = ref(false)

/** 截图编辑器是否可见 */
const showScreenshotEditor = ref(false)

/** html2canvas 生成的截图 Canvas */
let screenshotCanvas: HTMLCanvasElement | null = null

/** 常用表情列表 */
const commonEmojis = [
  '😀','😃','😄','😁','😆','😅','🤣','😂',
  '🙂','😊','😇','🥰','😍','🤩','😘','😗',
  '😚','😙','😋','😛','😜','🤪','😝','🤑',
  '🤗','🤭','🤫','🤔','🤐','🤨','😐','😑',
  '😶','😏','😒','🙄','😬','😮','🤯','😲',
  '😳','🥺','😢','😭','😤','😠','😡','🤬',
  '👍','👎','👏','🙌','🤝','💪','✌️','🤞',
  '❤️','🧡','💛','💚','💙','💜','💯','✅',
]

/** 当前字符数 */
const charCount = computed(() => inputText.value.length))

/** 是否可以发送 */
const canSend = computed(() => {
  return inputText.value.trim().length > 0 && !conversationStore.loading && charCount.value <= maxChars
})

/** 已在参与者列表中的ID */
const existingParticipantIds = computed(() => {
  const conv = conversationStore.currentConversation
  if (!conv) return []
  const ids: string[] = [conv.employee_id]
  if (conv.participants) {
    ids.push(...conv.participants.map((p: any) => p.id))
  }
  return ids
})

// ============================================================================
// 监听
// ============================================================================
watch(
  () => conversationStore.pendingReplyText,
  (newVal) => {
    if (newVal) {
      inputText.value = newVal
      conversationStore.pendingReplyText = ''
      nextTick(() => {
        inputRef.value?.focus()
      })
    }
  }
)

// ============================================================================
// 输入框高度自适应
// ============================================================================
function handleInput(): void {
  nextTick(() => {
    if (inputRef.value) {
      const scrollHeight = inputRef.value.scrollHeight
      const newHeight = Math.min(Math.max(scrollHeight, 60), 150)
      textareaHeight.value = newHeight
    }
  })
}

// ============================================================================
// 键盘事件
// ============================================================================
function handleKeydown(event: KeyboardEvent): void {
  if (event.key === 'Enter' && !event.shiftKey) {
    event.preventDefault()
    handleSend()
  }
}

// ============================================================================
// 发送消息
// ============================================================================
async function handleSend(): Promise<void> {
  const content = inputText.value.trim()
  if (!content || conversationStore.loading) return

  try {
    emit('send', content)
    inputText.value = ''
    textareaHeight.value = 60
  } catch (error) {
    console.error('发送消息失败:', error)
  }
}

// ============================================================================
// 粘贴事件
// ============================================================================
async function handlePaste(event: ClipboardEvent): Promise<void> {
  const items = event.clipboardData?.items
  if (!items) return

  for (const item of Array.from(items)) {
    if (item.kind === 'file') {
      event.preventDefault()
      const file = item.getAsFile()
      if (!file) continue

      if (file.type.startsWith('image/')) {
        await handleImageUpload(file)
      } else {
        await handleFileUpload(file)
      }
      return
    }
  }
}

// ============================================================================
// 文件上传
// ============================================================================
async function handleImageUpload(file: File | Blob): Promise<void> {
  const convId = conversationStore.currentConversation?.id
  if (!convId) {
    ElMessage.warning('请先选择一个会话')
    return
  }

  try {
    ElMessage.info('图片上传中...')
    const result = await uploadFile(file)
    const newMsg = await sendMessage(convId, '[图片]', 'image', {
      media_url: result.url,
      file_name: result.filename,
      file_size: result.file_size,
    })
    conversationStore.messages.push(newMsg)
    ElMessage.success('图片发送成功')
  } catch (error: any) {
    console.error('图片上传失败:', error)
    ElMessage.error(
      formatErrorDetail(error?.response?.data?.detail) ||
      error?.message ||
      '图片上传失败，请重试'
    )
  }
}

async function handleFileUpload(file: File | Blob): Promise<void> {
  const convId = conversationStore.currentConversation?.id
  if (!convId) {
    ElMessage.warning('请先选择一个会话')
    return
  }

  try {
    const fileName = file instanceof File ? file.name : '文件'
    ElMessage.info(`正在上传: ${fileName}`)
    const result = await uploadFile(file)
    const newMsg = await sendMessage(convId, `[文件] ${result.filename}`, 'file', {
      media_url: result.url,
      file_name: result.filename,
      file_size: result.file_size,
    })
    conversationStore.messages.push(newMsg)
    ElMessage.success('文件发送成功')
  } catch (error: any) {
    console.error('文件发送失败:', error)
    ElMessage.error(
      formatErrorDetail(error?.response?.data?.detail) ||
      error?.message ||
      '文件发送失败，请重试'
    )
  }
}

async function handleFileSelect(event: Event): Promise<void> {
  const input = event.target as HTMLInputElement
  const files = input.files
  if (!files || files.length === 0) return

  const convId = conversationStore.currentConversation?.id
  if (!convId) {
    ElMessage.warning('请先选择一个会话')
    return
  }

  for (const file of Array.from(files)) {
    try {
      if (file === files[0]) {
        ElMessage.info(`正在上传: ${file.name}`)
      }
      const result = await uploadFile(file)
      const isImage = result.msg_type === 'image'
      const newMsg = await sendMessage(
        convId,
        isImage ? '[图片]' : `[文件] ${file.name}`,
        isImage ? 'image' : 'file',
        {
          media_url: result.url,
          file_name: result.filename,
          file_size: result.file_size,
        }
      )
      conversationStore.messages.push(newMsg)
      ElMessage.success(`${file.name} 发送成功`)
    } catch (error: any) {
      console.error('文件上传失败:', error)
      ElMessage.error(
        `${file.name} 上传失败: ${
          formatErrorDetail(error?.response?.data?.detail) ||
          error?.message ||
          '请重试'
        }`
      )
    }
  }

  input.value = ''
}

// ============================================================================
// 工具栏
// ============================================================================
function handleToolbarClick(action: string): void {
  switch (action) {
    case 'image':
      if (fileInputRef.value) {
        fileInputRef.value.accept = 'image/*'
        fileInputRef.value.multiple = true
        fileInputRef.value.click()
      }
      break

    case 'file':
      if (fileInputRef.value) {
        fileInputRef.value.accept = ''
        fileInputRef.value.multiple = true
        fileInputRef.value.click()
      }
      break

    case 'quickReply':
      inputRef.value?.focus()
      break

    case 'screenshot':
      handleScreenshot()
      break

    default:
      const actionMap: Record<string, string> = {
        voice: '语音消息功能开发中',
      }
      ElMessage.info(actionMap[action] || '功能开发中')
  }
}

async function handleScreenshot(): Promise<void> {
  const convId = conversationStore.currentConversation?.id
  if (!convId) {
    ElMessage.warning('请先选择一个会话')
    return
  }

  try {
    ElMessage.info('正在截取页面...')
    const canvas = await html2canvas(document.body, {
      useCORS: true,
      scale: window.devicePixelRatio || 1,
      logging: false,
      backgroundColor: '#ffffff',
    })
    screenshotCanvas = canvas
    showScreenshotEditor.value = true
  } catch (error) {
    console.error('截图失败:', error)
    ElMessage.error('截图失败，请重试')
  }
}

async function onScreenshotConfirm(blob: Blob): Promise<void> {
  const convId = conversationStore.currentConversation?.id
  if (!convId) return

  try {
    ElMessage.info('截图上传中...')
    const result = await uploadFile(blob)
    const newMsg = await sendMessage(convId, '[截图]', 'image', {
      media_url: result.url,
      file_name: result.filename,
      file_size: result.file_size,
    })
    conversationStore.messages.push(newMsg)
    ElMessage.success('截图发送成功')
  } catch (error: any) {
    console.error('[InputBox] 截图发送失败:', error)
    ElMessage.error(
      `截图发送失败：${
        formatErrorDetail(error?.response?.data?.detail) ||
        error?.message ||
        '未知错误'
      }`
    )
  } finally {
    showScreenshotEditor.value = false
    screenshotCanvas = null
  }
}

function onScreenshotCancel(): void {
  showScreenshotEditor.value = false
  screenshotCanvas = null
}

function onEmojiSelect(emoji: string): void {
  inputText.value += emoji
  showEmojiPicker.value = false
  nextTick(() => {
    inputRef.value?.focus()
  })
}

function onInviteSuccess(): void {
  conversationStore.fetchConversations()
}
</script>

<style scoped>
/* 整体容器 */
.input-box {
  padding: 0 12px 8px 12px;
  background: var(--bg-primary);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  position: relative;
}

/* 快捷工具栏 */
.chat-toolbar {
  display: flex;
  align-items: center;
  gap: 1px;
  padding: 8px 6px 4px 6px;
  flex-shrink: 0;
}

.tb-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 28px;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 14px;
  color: var(--text-muted);
  transition: all 0.2s;
  position: relative;
}

.tb-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
}

.tb-tip {
  display: none;
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 6px;
  background: var(--text-primary);
  color: var(--bg-secondary);
  font-size: 10px;
  padding: 3px 8px;
  border-radius: 4px;
  white-space: nowrap;
  pointer-events: none;
}

.tb-btn:hover .tb-tip {
  display: block;
}

.tb-sep {
  width: 1px;
  height: 14px;
  background: var(--border);
  margin: 0 6px;
  flex-shrink: 0;
}

/* 输入卡片 */
.chat-input-card {
  display: flex;
  align-items: flex-end;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color 0.25s, box-shadow 0.25s;
}

.chat-input-card:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* 输入框 */
.chat-input {
  flex: 1;
  background: transparent;
  border: none;
  padding: 10px 0 10px 14px;
  color: var(--text-primary);
  font-size: 13px;
  font-family: inherit;
  resize: none;
  min-height: 60px;
  max-height: 150px;
  outline: none;
  line-height: 1.5;
  overflow-y: auto;
}

.chat-input:focus {
  outline: none;
}

.chat-input::placeholder {
  color: var(--text-muted);
}

/* 发送按钮 */
.btn-send {
  width: 36px;
  height: 36px;
  border: none;
  background: var(--bg-tertiary);
  border-radius: 50%;
  cursor: not-allowed;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 4px 6px 4px 0;
  flex-shrink: 0;
  transition: all 0.25s;
  padding: 0;
}

.btn-send--active {
  background: linear-gradient(135deg, var(--accent), var(--purple));
  cursor: pointer;
}

.btn-send--active:hover {
  opacity: 0.9;
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.send-icon {
  width: 18px;
  height: 18px;
  color: var(--text-placeholder);
}

.btn-send--active .send-icon {
  color: #fff;
}

/* 字数统计 */
.input-box__counter {
  text-align: right;
  font-size: 11px;
  color: var(--text-placeholder);
  margin-top: 4px;
}

/* 表情选择器 */
.emoji-wrapper {
  position: relative;
}

.emoji-picker-popup {
  position: absolute;
  bottom: 36px;
  left: 0;
  z-index: 100;
  background: var(--bg-secondary);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px;
  max-height: 240px;
  overflow-y: auto;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}

.emoji-grid {
  display: grid;
  grid-template-columns: repeat(8, 32px);
  gap: 2px;
}

.emoji-grid__item {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  border: none;
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.15s;
  outline: none;
  padding: 0;
}

.emoji-grid__item:hover {
  background: var(--accent-soft);
}

.emoji-picker-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 99;
}

/* 引用回复预览 */
.reply-preview {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: var(--bg-tertiary);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.reply-preview-bar {
  width: 3px;
  height: 28px;
  border-radius: 2px;
  background: var(--accent);
  flex-shrink: 0;
}

.reply-preview-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.reply-preview-sender {
  font-size: 12px;
  color: var(--accent);
  font-weight: 500;
}

.reply-preview-text {
  font-size: 12px;
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reply-preview-close {
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  border-radius: 4px;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.2s;
}

.reply-preview-close:hover {
  background: var(--bg-secondary);
  color: var(--text-primary);
}
</style>