<template>
  <div
    :class="assistantClass"
    :style="{ left: position.left + 'px', top: position.top + 'px' }"
    class="ai-assistant">
    <div class="ai-header" @mousedown="startDrag">
      <div class="ai-title">
        <span class="ai-dot"/>
        <span v-if="!collapsed">品质管理AI助手</span>
      </div>
      <div class="ai-actions">
        <el-tooltip v-if="!collapsed" :content="'窗口尺寸：' + sizeLabel" effect="dark" placement="top">
          <el-button class="ai-icon-button" type="text" icon="el-icon-rank" @mousedown.stop @click.stop="toggleSize"/>
        </el-tooltip>
        <el-tooltip v-if="!collapsed" effect="dark" content="清空聊天" placement="top">
          <el-button class="ai-icon-button" type="text" icon="el-icon-delete" @mousedown.stop @click.stop="clearMessages"/>
        </el-tooltip>
        <el-tooltip :content="collapsed ? '显示助手' : '隐藏助手'" effect="dark" placement="top">
          <el-button :icon="collapsed ? 'el-icon-plus' : 'el-icon-minus'" class="ai-icon-button" type="text" @mousedown.stop @click.stop="toggleCollapsed"/>
        </el-tooltip>
      </div>
    </div>

    <div v-show="!collapsed" class="ai-body">
      <div ref="messages" class="ai-messages">
        <div v-for="item in messages" :key="item.id" :class="'is-' + item.role" class="ai-message">
          <div class="ai-message-title">{{ item.role === 'user' ? '问题' : '回答' }}</div>
          <div v-if="item.role === 'assistant'" class="ai-message-content">
            <div v-html="item.content"/>
            <div v-for="chart in item.charts" :key="chart.id" class="ai-chart-wrap">
              <div :ref="'chart_' + chart.id" class="ai-chart"/>
            </div>
          </div>
          <div v-else class="ai-message-content">{{ item.content }}</div>
        </div>
      </div>
      <div class="ai-input-row">
        <el-input
          v-model="question"
          :autosize="{ minRows: 2, maxRows: 4 }"
          type="textarea"
          resize="none"
          placeholder="询问质量数据、SPC趋势图、项目页面或功能..."
          @keydown.native="handleInputKeydown"/>
        <el-button :loading="loading" type="primary" icon="el-icon-position" @click="sendQuestion">发送</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import axios from 'axios'

function welcomeMessage() {
  return {
    id: 1,
    role: 'assistant',
    content: '<table class="ai-result-table"><tbody><tr><th>状态</th><td>AI助手已就绪</td></tr><tr><th>能力</th><td>可查询项目功能、质量管理模块、SPC趋势/直方/正态/过程能力/X-R/X-S图</td></tr><tr><th>输入</th><td>按 Enter 发送，Shift + Enter 换行</td></tr></tbody></table>',
    charts: []
  }
}

export default {
  name: 'DeepSeekAssistant',
  data() {
    return {
      collapsed: false,
      size: 'medium',
      question: '',
      loading: false,
      position: {
        left: Math.max(0, window.innerWidth - 470),
        top: 96
      },
      drag: {
        active: false,
        startX: 0,
        startY: 0,
        left: 0,
        top: 0
      },
      messages: [welcomeMessage()],
      messageId: 2,
      chartInstances: {},
      lastSpcWarning: null
    }
  },
  computed: {
    assistantClass() {
      const result = { 'is-collapsed': this.collapsed }
      result['is-' + this.size] = true
      return result
    },
    sizeLabel() {
      return ({ small: '小', medium: '中', large: '大' })[this.size] || '中'
    }
  },
  mounted() {
    this.keepInViewport()
    window.addEventListener('mousemove', this.onDrag)
    window.addEventListener('mouseup', this.stopDrag)
    window.addEventListener('resize', this.handleResize)
  },
  beforeDestroy() {
    window.removeEventListener('mousemove', this.onDrag)
    window.removeEventListener('mouseup', this.stopDrag)
    window.removeEventListener('resize', this.handleResize)
    Object.keys(this.chartInstances).forEach(key => {
      this.chartInstances[key].dispose()
    })
  },
  methods: {
    getBaseUrl() {
      return (window.g && window.g.aiMcpURL) || 'http://127.0.0.1:3100'
    },
    toggleCollapsed() {
      this.collapsed = !this.collapsed
      this.$nextTick(() => {
        this.keepInViewport()
        this.resizeChartsAfterLayout()
      })
    },
    toggleSize() {
      const sizes = ['small', 'medium', 'large']
      const index = sizes.indexOf(this.size)
      this.size = sizes[(index + 1) % sizes.length]
      this.$nextTick(() => {
        this.keepInViewport()
        this.resizeChartsAfterLayout()
      })
    },
    clearMessages() {
      Object.keys(this.chartInstances).forEach(key => {
        this.chartInstances[key].dispose()
      })
      this.chartInstances = {}
      this.messages = [welcomeMessage()]
      this.messageId = 2
      this.question = ''
      this.scrollToBottom()
    },
    startDrag(event) {
      this.drag.active = true
      this.drag.startX = event.clientX
      this.drag.startY = event.clientY
      this.drag.left = this.position.left
      this.drag.top = this.position.top
    },
    onDrag(event) {
      if (!this.drag.active) return
      this.position.left = this.drag.left + event.clientX - this.drag.startX
      this.position.top = this.drag.top + event.clientY - this.drag.startY
      this.keepInViewport()
    },
    stopDrag() {
      this.drag.active = false
    },
    getPanelSize() {
      const sizes = {
        small: { width: 520, height: 560 },
        medium: { width: 720, height: 680 },
        large: { width: 960, height: 780 }
      }
      return this.collapsed ? { width: 56, height: 48 } : sizes[this.size] || sizes.medium
    },
    keepInViewport() {
      const panelSize = this.getPanelSize()
      this.position.left = Math.max(0, Math.min(this.position.left, window.innerWidth - panelSize.width - 8))
      this.position.top = Math.max(0, Math.min(this.position.top, window.innerHeight - panelSize.height - 8))
    },
    handleResize() {
      this.keepInViewport()
      this.resizeChartsAfterLayout()
    },
    scrollToBottom() {
      this.$nextTick(() => {
        if (this.$refs.messages) this.$refs.messages.scrollTop = this.$refs.messages.scrollHeight
      })
    },
    sanitizeHtml(html) {
      return String(html || '')
        .replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, '')
        .replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, '')
        .replace(/\son[a-z]+\s*=\s*'[^']*'/gi, '')
        .replace(/javascript:/gi, '')
    },
    escapeHtml(value) {
      return String(value || '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
    },
    handleInputKeydown(event) {
      if (event.key === 'Enter' && !event.shiftKey) {
        event.preventDefault()
        this.sendQuestion()
      }
    },
    sendQuestion() {
      const content = this.question.trim()
      if (!content || this.loading) return
      this.messages.push({ id: this.messageId++, role: 'user', content })
      this.question = ''
      this.loading = true
      this.scrollToBottom()
      axios.post(this.getBaseUrl() + '/api/assistant/chat', {
        question: content,
        context: {
          lastSpcWarning: this.lastSpcWarning
        }
      }).then(response => {
        const payload = response.data || {}
        if (payload.data && payload.data.analysis) {
          this.lastSpcWarning = {
            analysis: payload.data.analysis,
            updatedAt: new Date().toISOString()
          }
        }
        const message = {
          id: this.messageId++,
          role: 'assistant',
          content: this.sanitizeHtml(payload.html || '<p>未返回内容</p>'),
          charts: Array.isArray(payload.charts) ? payload.charts : []
        }
        this.messages.push(message)
        this.handleAssistantActions(payload.actions)
        this.$nextTick(() => this.renderCharts(message))
      }).catch(error => {
        this.messages.push({
          id: this.messageId++,
          role: 'assistant',
          content: '<table class="ai-result-table"><tbody><tr><th>错误</th><td>' + this.escapeHtml(error.message) + '</td></tr><tr><th>检查项</th><td>确认 MCP 服务是否已启动，默认地址 http://127.0.0.1:3100</td></tr></tbody></table>',
          charts: []
        })
      }).then(() => {
        this.loading = false
        this.scrollToBottom()
      })
    },
    handleAssistantActions(actions) {
      if (!Array.isArray(actions)) return
      actions.forEach(action => {
        if (!action || action.type !== 'navigate' || !action.path) return
        if (action.url) {
          window.location.href = action.url
          return
        }
        this.$router.push({ path: action.path }).catch(() => {})
      })
    },
    renderCharts(message) {
      if (!message || !Array.isArray(message.charts)) return
      message.charts.forEach(chart => {
        const ref = this.$refs['chart_' + chart.id]
        const el = Array.isArray(ref) ? ref[0] : ref
        if (!el || !chart.option || !this.$echarts) return
        const instance = this.$echarts.init(el)
        instance.setOption(chart.option)
        this.chartInstances[chart.id] = instance
      })
      this.resizeChartsAfterLayout()
    },
    resizeCharts() {
      Object.keys(this.chartInstances).forEach(key => {
        this.chartInstances[key].resize()
      })
    },
    resizeChartsAfterLayout() {
      this.$nextTick(() => {
        this.resizeCharts()
        window.setTimeout(this.resizeCharts, 80)
        window.setTimeout(this.resizeCharts, 220)
      })
    }
  }
}
</script>

<style rel="stylesheet/scss" lang="scss">
.ai-assistant {
  position: fixed;
  z-index: 3000;
  background: #fff;
  border: 1px solid #dcdfe6;
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 21, 41, 0.16);
  overflow: hidden;
  transition: width .18s ease, height .18s ease, box-shadow .18s ease;

  &.is-small {
    width: 520px;
    height: 560px;
  }

  &.is-medium {
    width: 720px;
    height: 680px;
  }

  &.is-large {
    width: 960px;
    height: 780px;
  }

  &.is-collapsed {
    width: 56px;
    height: 48px;
    border-color: #1890ff;
    box-shadow: 0 4px 14px rgba(24, 144, 255, 0.24);
  }
}

.ai-header {
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 10px;
  cursor: move;
  color: #fff;
  background: #304156;
  user-select: none;
}

.ai-title {
  display: flex;
  align-items: center;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
}

.ai-dot {
  width: 8px;
  height: 8px;
  margin-right: 8px;
  border-radius: 50%;
  background: #67c23a;
  flex: 0 0 auto;
}

.ai-actions {
  display: flex;
  align-items: center;
}

.ai-icon-button {
  color: #fff;
  padding: 6px;
}

.ai-icon-button:hover,
.ai-icon-button:focus {
  color: #d9ecff;
}

.ai-body {
  height: calc(100% - 48px);
  display: flex;
  flex-direction: column;
  background: #f5f7fa;
}

.ai-messages {
  flex: 1;
  overflow: auto;
  padding: 10px;
}

.ai-message {
  margin-bottom: 10px;
  padding: 8px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  background: #fff;
}

.ai-message.is-user {
  background: #ecf5ff;
  border-color: #d9ecff;
}

.ai-message-title {
  margin-bottom: 6px;
  font-size: 12px;
  color: #909399;
}

.ai-message-content {
  font-size: 13px;
  line-height: 1.5;
  color: #303133;
  word-break: break-word;
}

.ai-input-row {
  display: grid;
  grid-template-columns: 1fr 72px;
  gap: 8px;
  padding: 10px;
  border-top: 1px solid #ebeef5;
  background: #fff;
}

.ai-result-table {
  width: 100%;
  border-collapse: collapse;
  margin: 6px 0 10px;
  background: #fff;
  font-size: 12px;
}

.ai-result-table th,
.ai-result-table td {
  border: 1px solid #dcdfe6;
  padding: 6px 8px;
  text-align: left;
  vertical-align: top;
}

.ai-result-table th {
  background: #f2f6fc;
  color: #606266;
  font-weight: 600;
}

.ai-risk-badge {
  display: inline-flex;
  align-items: center;
  min-width: 48px;
  height: 22px;
  padding: 0 8px;
  border-radius: 3px;
  font-size: 12px;
  font-weight: 600;
  line-height: 22px;
}

.ai-risk-normal {
  color: #166534;
  background: #dcfce7;
  border: 1px solid #86efac;
}

.ai-risk-watch {
  color: #854d0e;
  background: #fef9c3;
  border: 1px solid #fde68a;
}

.ai-risk-warning {
  color: #9a3412;
  background: #ffedd5;
  border: 1px solid #fdba74;
}

.ai-risk-critical {
  color: #b91c1c;
  background: #fee2e2;
  border: 1px solid #fca5a5;
}

.ai-detail-table-wrap {
  width: 100%;
  overflow-x: auto;
  margin: 6px 0 12px;
  background: #fff;
}

.ai-detail-table {
  width: 100%;
  min-width: 704px;
  border-collapse: collapse;
  table-layout: fixed;
  background: #fff;
  color: #303133;
  font-size: 12px;
}

.ai-detail-table th,
.ai-detail-table td {
  border: 1px solid #d8dee9;
  padding: 6px 8px;
  text-align: left;
  vertical-align: top;
  line-height: 18px;
  word-break: break-word;
  white-space: normal;
}

.ai-detail-table th {
  background: #eef3fb;
  color: #4b5563;
  font-weight: 600;
}

.ai-detail-table .ai-status-ng {
  color: #ff0000;
  font-weight: 600;
}

.ai-chart-wrap {
  margin: 12px 0;
  padding: 10px;
  border: 1px solid #d8dee9;
  border-radius: 6px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(31, 41, 55, 0.06);
  overflow: hidden;
}

.ai-chart {
  width: 100%;
  height: 340px;
}
</style>
